﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

// Формирует имя файла подписи по шаблону.
//
Функция ИмяФайлаПодписи(ИмяБезРасширения, КомуВыданСертификат, РасширениеДляФайловПодписи, ТребуетсяРазделитель = Истина) Экспорт
	
	Разделитель = ?(ТребуетсяРазделитель, " - ", " ");
	
	ИмяФайлаПодписиБезРасширения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1%2%3",
		ИмяБезРасширения, Разделитель, КомуВыданСертификат);
	
	Если СтрДлина(ИмяФайлаПодписиБезРасширения) > 120 Тогда
		ИмяФайлаПодписиБезРасширения = ЭлектроннаяПодписьСлужебныйВызовСервера.СокращенноеИмяФайла(
			ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(ИмяФайлаПодписиБезРасширения), 120);
	КонецЕсли;
	
	ИмяФайлаПодписи = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1.%2",
		ИмяФайлаПодписиБезРасширения, РасширениеДляФайловПодписи);
	
	Возврат ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(ИмяФайлаПодписи);

КонецФункции

// Формирует имя файла сертификата по шаблону.
//
Функция ИмяФайлаСертификата(ИмяБезРасширения, КомуВыданСертификат, РасширениеДляФайловСертификата, ТребуетсяРазделитель = Истина) Экспорт
	
	Если Не ЗначениеЗаполнено(КомуВыданСертификат) Тогда
		ИмяФайлаСертификатаБезРасширения = ИмяБезРасширения;
	Иначе
		Разделитель = ?(ТребуетсяРазделитель, " - ", " ");
		ИмяФайлаСертификатаБезРасширения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1%2%3",
			ИмяБезРасширения, Разделитель, КомуВыданСертификат);
	КонецЕсли;
	
	Если СтрДлина(ИмяФайлаСертификатаБезРасширения) > 120 Тогда
		ИмяФайлаСертификатаБезРасширения = ЭлектроннаяПодписьСлужебныйВызовСервера.СокращенноеИмяФайла(
			ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(ИмяФайлаСертификатаБезРасширения), 120);
	КонецЕсли;
	
	ИмяФайлаСертификата = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1.%2",
		ИмяФайлаСертификатаБезРасширения, РасширениеДляФайловСертификата);
	
	Возврат ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(ИмяФайлаСертификата);
	
КонецФункции

// Конструктор результата проверки по списку удостоверяющих центров.
// 
// Возвращаемое значение:
//  Структура - результат проверки удостоверяющего центра по умолчанию:
//   * Действует - Булево - аккредитованный УЦ действует на дату проверки или проверка не выполнялась (сертификат 
//                 неквалифицированный или УЦ не найден в списке аккредитованных)
//   * НайденВСпискеУдостоверяющихЦентров - Булево - сертификат, выданный УЦ является квалифицированным
//   * Государственный - Булево - относится к определенному списку УЦ, выдающих сертификаты, по которым не нужно
//                                выполнять некоторые проверки.
//   например, в России: УЦ ФНС России, Казначейства России, и Банка России.
//   * ЭтоКвалифицированныйСертификат - Булево - сертификат выдан в период аккредитации УЦ.
//   * Предупреждение - см. ПредупреждениеПриПроверкеУдостоверяющегоЦентраСертификата
//
Функция РезультатПроверкиУдостоверяющегоЦентраПоУмолчанию() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Действует", Истина);
	Результат.Вставить("НайденВСпискеУдостоверяющихЦентров", Ложь);
	Результат.Вставить("Государственный", Ложь);
	Результат.Вставить("ЭтоКвалифицированныйСертификат", Ложь);
	Результат.Вставить("Предупреждение", ПредупреждениеПриПроверкеУдостоверяющегоЦентраСертификата());
	
	Возврат Результат;
	
КонецФункции

// Возвращаемое значение:
//   Структура - сообщение об ошибке/предупреждение для проверяемого сертификата:
//   * ТекстОшибки - Строка
//   * ВозможенПеревыпуск - Булево - можно подать заявление на новый сертификат из программы.
//   * Причина - Строка - причина ошибки для отображения в расширенной форме ошибки.
//   * Решение - Строка - рекомендация для отображения в расширенной форме ошибки.
//
Функция ПредупреждениеПриПроверкеУдостоверяющегоЦентраСертификата() Экспорт
	
	Предупреждение = Новый Структура;
	Предупреждение.Вставить("ТекстОшибки", "");
	Предупреждение.Вставить("ВозможенПеревыпуск", Ложь);
	Предупреждение.Вставить("Причина", "");
	Предупреждение.Вставить("Решение", "");
	Предупреждение.Вставить("ДополнительныеСведения", "");
	
	Возврат Предупреждение;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//   * ТекстОшибки - Строка
//
Функция ТекстОшибкиНеУдалосьОпределитьПрограмму(Ошибка) Экспорт
	
	Если ЗначениеЗаполнено(Ошибка) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось автоматически определить программу для работы с электронной подписью:
			|%1'"), Ошибка);
	Иначе
		ТекстОшибки = НСтр("ru = 'Не удалось автоматически определить программу для работы с электронной подписью.'");
	КонецЕсли;
	
	Возврат ТекстОшибки;
	
КонецФункции

// Возвращаемое значение:
//   Строка
//   Массив из см. НовоеРасширенноеОписаниеПрограммы
//
Функция РезультатПоискаКриптопровайдеров(КриптопровайдерыРезультат, ИмяСервера = "") Экспорт
	
	Если ИмяСервера = "" Тогда
		ИмяСервера = НСтр("ru = 'На компьютере'");
	Иначе
		ИмяСервера = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'На сервере %1'"), ИмяСервера);
	КонецЕсли;
	
	Если КриптопровайдерыРезультат = Неопределено Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1 не удалось автоматически определить установленные программы,
						|проверьте настройки программ.'"), ИмяСервера);
	ИначеЕсли КриптопровайдерыРезультат.ПроверкаВыполнена Тогда
		Если КриптопровайдерыРезультат.Криптопровайдеры.Количество() > 0 Тогда
			Возврат КриптопровайдерыРезультат.Криптопровайдеры;
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1 не установлены программы для работы с электронной подписью.'"), ИмяСервера);
		КонецЕсли;
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '%1 не удалось автоматически определить установленные программы:
					|%2'"), ИмяСервера, КриптопровайдерыРезультат.Ошибка);
	КонецЕсли;
	
	Возврат ТекстОшибки;
	
КонецФункции

// Адрес списка отзыва, расположенного на другом ресурсе.
// 
// Параметры:
//  ИмяИздателя - Строка - имя издателя латиницей
//  Сертификат  - ДвоичныеДанные
//              - Строка
// 
// Возвращаемое значение:
//  Структура:
//   * АдресВнутренний - Строка - идентификатор для поиска в базе
//   * АдресВнешний - Строка - адрес ресурса для скачивания
//
Функция АдресСпискаОтзываВнутренний(ИмяИздателя, Сертификат) Экспорт
	
	Возврат ЭлектроннаяПодписьКлиентСерверЛокализация.АдресСпискаОтзываВнутренний(ИмяИздателя, Сертификат);
	
КонецФункции

// Определить тип криптографических данных.
// 
// Параметры:
//  Данные - ДвоичныеДанные
//         - Строка - адрес данных
// 
// Возвращаемое значение:
//  Неопределено, Строка - "Подпись", "ЗашифрованныеДанные" или "Сертификат"
//
Функция ОпределитьТипДанных(Данные) Экспорт
	
	ДвоичныеДанные = ДвоичныеДанныеИзДанных(Данные,
		"ЭлектроннаяПодписьСлужебныйКлиентСервер.ОпределитьТипДанных");
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
	// SEQUENCE (PKCS #7 ContentInfo).
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
	
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат Неопределено;
	КонецЕсли;

	// OBJECT IDENTIFIER (contentType).
	ПропуститьНачалоБлока(АнализДанных, 0, 6);
	
	Если Не АнализДанных.ЕстьОшибка Тогда
		РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
		Если РазмерДанных = 9 Тогда
			Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
			СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
			Если СтрокаБуфера = "2A864886F70D010702" Тогда // 1.2.840.113549.1.7.2 signedData (PKCS #7).
				Возврат "Подпись";
			ИначеЕсли СтрокаБуфера = "2A864886F70D010703" Тогда // 1.2.840.113549.1.7.3 envelopedData (PKCS #7)
				Возврат "ЗашифрованныеДанные";
			КонецЕсли;
		Иначе
			Возврат Неопределено;
		КонецЕсли;
	Иначе
		АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
		// SEQUENCE (PKCS #7 ContentInfo).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);
			// SEQUENCE (tbsCertificate).
			ПропуститьНачалоБлока(АнализДанных, 0, 16);
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат Неопределено;
		КонецЕсли;
		Возврат "Сертификат";
	КонецЕсли;
		
	Возврат Неопределено;
	
КонецФункции

// Сертификаты по порядку до корневого.
// 
// Параметры:
//  Сертификаты - Массив из СертификатКриптографии
// 
// Возвращаемое значение:
//  Массив из СертификатКриптографии - сертификаты по порядку до корневого
//
Функция СертификатыПоПорядкуДоКорневого(Сертификаты) Экспорт
	
	ПоПорядку = Новый Массив;
	СертификатыПоСубъектам = Новый Соответствие;
	ОписаниеСертификатов = Новый Соответствие;
	
	Для Каждого Сертификат Из Сертификаты Цикл
		ОписаниеСертификатов.Вставить(Сертификат, Сертификат);
		ПоПорядку.Добавить(Сертификат);
		СертификатыПоСубъектам.Вставить(КлючИздателя(Сертификат.Субъект), Сертификат);
	КонецЦикла;
	
	Для Счетчик = 1 По ПоПорядку.Количество() Цикл
		ЕстьИзменения = Ложь;
		УпорядочитьСертификаты(
			ПоПорядку, ОписаниеСертификатов, СертификатыПоСубъектам, ЕстьИзменения); 
		Если Не ЕстьИзменения Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;

	Возврат ПоПорядку;
	
КонецФункции

Функция АктуальныеАлгоритмыПрограмм() Экспорт
	
	Возврат ИменаАлгоритмовПодписиГОСТ_34_10_2012_256()
	
КонецФункции

// Описание подключения внешней компоненты (ExtraCryptoAPI).
//
// Возвращаемое значение:
//  Структура:
//   * ПолноеИмяМакета - Строка
//   * ИмяОбъекта      - Строка
//
Функция ОписаниеКомпоненты() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("ИмяОбъекта", "ExtraCryptoAPI");
	Параметры.Вставить("ПолноеИмяМакета",
		"Справочник.СертификатыКлючейЭлектроннойПодписиИШифрования.Макет.КомпонентаExtraCryptoAPI");
	Возврат Параметры;
	
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Конструктор для чтения свойств подписи.
// 
// Возвращаемое значение:
//   см. ЭлектроннаяПодпись.СвойстваПодписи
//
Функция РезультатЧтенияСвойствПодписи() Экспорт
	
	Структура = Новый Структура;
	Структура.Вставить("Успех", Неопределено);
	Структура.Вставить("ТекстОшибки", "");
	
	ОбщегоНазначенияКлиентСервер.ДополнитьСтруктуру(
		Структура, СвойстваПодписиПриЧтенииИПроверке());
	Структура.Вставить("Сертификаты", Новый Массив);
		
	Возврат Структура;
	
КонецФункции

Функция СвойстваПодписиПриЧтенииИПроверке() Экспорт
	
	Структура = Новый Структура;
	Структура.Вставить("ТипПодписи");
	Структура.Вставить("СрокДействияПоследнейМеткиВремени");
	Структура.Вставить("ДатаПодписиИзМетки");
	Структура.Вставить("НеподтвержденнаяДатаПодписи");
	
	Структура.Вставить("Сертификат");
	Структура.Вставить("Отпечаток");
	Структура.Вставить("КомуВыданСертификат");
	
	Возврат Структура;
	
КонецФункции

Процедура УпорядочитьСертификаты(ПоПорядку, ОписаниеСертификатов, СертификатыПоСубъектам, ЕстьИзменения) Экспорт
	
	Для Каждого ОписаниеСертификата Из ОписаниеСертификатов Цикл
		
		СвойстваСертификата = ОписаниеСертификата.Ключ;
		Сертификат = ОписаниеСертификата.Значение;
	
		КлючИздателя = КлючИздателя(СвойстваСертификата.Издатель);
		СертификатИздателя = СертификатыПоСубъектам.Получить(КлючИздателя);
		
		Позиция = ПоПорядку.Найти(Сертификат);
		

		Если СвойстваСертификата.Издатель.CN = СвойстваСертификата.Субъект.CN
			И КлючИздателя = КлючИздателя(СвойстваСертификата.Субъект)
			Или СертификатИздателя = Неопределено Тогда

			Если Позиция <> ПоПорядку.ВГраница() Тогда
				ПоПорядку.Удалить(Позиция);
				ПоПорядку.Добавить(Сертификат);
				ЕстьИзменения = Истина;
			КонецЕсли;
			Продолжить;
		КонецЕсли;

		ПозицияИздателя = ПоПорядку.Найти(СертификатИздателя);
		Если Позиция + 1 = ПозицияИздателя Тогда
			Продолжить;
		КонецЕсли;
		
		ПоПорядку.Удалить(Позиция);
		ЕстьИзменения = Истина;
		ПозицияИздателя = ПоПорядку.Найти(СертификатИздателя);
		ПоПорядку.Вставить(ПозицияИздателя, Сертификат);
		
	КонецЦикла;
	
КонецПроцедуры 

Функция КлючИздателя(ИздательИлиСубъект) Экспорт
	Массив = Новый Массив;
	Для Каждого КлючИЗначение Из ИздательИлиСубъект Цикл
		Массив.Добавить(КлючИЗначение.Ключ);
		Массив.Добавить(КлючИЗначение.Значение);
	КонецЦикла;
	Возврат ИздательИлиСубъект.CN + СтрСоединить(Массив);
КонецФункции

Функция ПользователиСертификатаСтрокой(Пользователь1, Пользователь2, КоличествоПользователей) Экспорт
	
	СтрокаПользователей = СтрШаблон("%1, %2", Пользователь1, Пользователь2);
	Если КоличествоПользователей > 2 Тогда
		СтрокаПользователей = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1 и другие (всего %2)'"), СтрокаПользователей, Формат(КоличествоПользователей, "ЧГ=0"));
	КонецЕсли;

	Возврат СтрокаПользователей;
	
КонецФункции

Функция ОписаниеПрограммыПоИмениКриптопровайдера(ИмяКриптопровайдера, ОписанияПрограмм, ПрограммыАвто) Экспорт
	
	ПрограммаНайдена = Ложь;
	
	Если ЗначениеЗаполнено(ПрограммыАвто) Тогда
		Для Каждого ОписаниеПрограммы Из ПрограммыАвто Цикл
			Если ОписаниеПрограммы.ИмяПрограммы = ИмяКриптопровайдера Тогда
				ПрограммаНайдена = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если Не ПрограммаНайдена Тогда
		Для Каждого ОписаниеПрограммы Из ОписанияПрограмм Цикл
			Если ОписаниеПрограммы.ИмяПрограммы = ИмяКриптопровайдера Тогда
				ПрограммаНайдена = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если ПрограммаНайдена Тогда
		Возврат ОписаниеПрограммы;
	КонецЕсли;
	
	ОписаниеПрограммы = Неопределено;
	
	Если ИмяКриптопровайдера = "Crypto-Pro GOST R 34.10-2001 KC1 CSP"
	 Или ИмяКриптопровайдера = "Crypto-Pro GOST R 34.10-2001 KC2 CSP" Тогда
		
		ОписаниеПрограммы = ОписаниеПрограммыПоИмениКриптопровайдера(
			"Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider", ОписанияПрограмм, ПрограммыАвто);
		
	ИначеЕсли ИмяКриптопровайдера = "Crypto-Pro GOST R 34.10-2012 KC1 CSP"
	      Или ИмяКриптопровайдера = "Crypto-Pro GOST R 34.10-2012 KC2 CSP" Тогда
		
		ОписаниеПрограммы = ОписаниеПрограммыПоИмениКриптопровайдера(
			"Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider", ОписанияПрограмм, ПрограммыАвто);
		
	ИначеЕсли ИмяКриптопровайдера = "Crypto-Pro GOST R 34.10-2012 KC1 Strong CSP"
	      Или ИмяКриптопровайдера = "Crypto-Pro GOST R 34.10-2012 KC2 Strong CSP" Тогда
		
		ОписаниеПрограммы = ОписаниеПрограммыПоИмениКриптопровайдера(
			"Crypto-Pro GOST R 34.10-2012 Strong Cryptographic Service Provider", ОписанияПрограмм, ПрограммыАвто);
	КонецЕсли;
	
	Возврат ОписаниеПрограммы;
	
КонецФункции

Функция СвойстваСертификатаИзОтветаКомпоненты(ОтветКомпоненты) Экспорт
	
	СвойстваСертификата = Новый Структура;
	СвойстваСертификата.Вставить("АдресаСписковОтзыва", Новый Массив);
	
	Попытка
		СвойстваСертификатаРезультат = ПрочитатьОтветКомпоненты(
			ОтветКомпоненты);
			
		АдресаСписковОтзыва = СвойстваСертификатаРезультат.Получить("crls");
		Если ЗначениеЗаполнено(АдресаСписковОтзыва) Тогда
			СвойстваСертификата.АдресаСписковОтзыва = АдресаСписковОтзыва;
		КонецЕсли;

		СвойстваСертификата.Вставить("Издатель", СвойстваСертификатаРезультат.Получить("issuer_name"));
		СвойстваСертификата.Вставить("АлгоритмОткрытогоКлюча", СвойстваСертификатаРезультат.Получить(
			"public_key_algorithm"));
		СвойстваСертификата.Вставить("АлгоритмПодписи", СвойстваСертификатаРезультат.Получить("signature_algorithm"));
		СвойстваСертификата.Вставить("СерийныйНомер", СвойстваСертификатаРезультат.Получить("serial_number"));
		СвойстваСертификата.Вставить("ИмяКонтейнера", СвойстваСертификатаРезультат.Получить("container_name"));
		СвойстваСертификата.Вставить("ОписаниеПрограммы", СвойстваСертификатаРезультат.Получить("provider"));
		СвойстваСертификата.Вставить("Сертификат", СвойстваСертификатаРезультат.Получить("value"));
		СвойстваСертификата.Вставить("ОткрытыйКлюч", СвойстваСертификатаРезультат.Получить("public_key"));

		Возврат СвойстваСертификата;

	Исключение

		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при чтении расширенных свойств сертификата:
				 | %1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;

КонецФункции

Функция ЦепочкаСертификатовИзОтветаКомпоненты(ОтветКомпоненты, ИдентификаторФормы) Экспорт
	
	Результат = Новый Структура("Сертификаты, Ошибка", Новый Массив, "");
	
	Попытка
		СертификатыРезультат = ПрочитатьОтветКомпоненты(
			ОтветКомпоненты);
		СертификатыРезультат = СертификатыРезультат.Получить("Certificates");
	Исключение
		
		Результат.Ошибка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при получении цепочки сертификатов %1'"),
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Результат;
		
	КонецПопытки;
		
	Для Каждого ТекущийСертификат Из СертификатыРезультат Цикл
		
		ОписаниеСертификата = Новый Структура;
		ОписаниеСертификата.Вставить("Субъект", ТекущийСертификат.Получить("subject_name"));
		ДанныеСертификата = ТекущийСертификат.Получить("value");
		Если ИдентификаторФормы = Неопределено Тогда
			ОписаниеСертификата.Вставить("ДанныеСертификата", ДанныеСертификата);
		Иначе
			ОписаниеСертификата.Вставить("ДанныеСертификата",
				ПоместитьВоВременноеХранилище(ДанныеСертификата, ИдентификаторФормы));
		КонецЕсли;
		
		ОписаниеСертификата.Вставить("Издатель", ТекущийСертификат.Получить("issuer_name"));
		ОписаниеСертификата.Вставить("ОткрытыйКлюч", ТекущийСертификат.Получить("public_key_"));
		
		Результат.Сертификаты.Добавить(ОписаниеСертификата);
		
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция УстановленныеКриптопровайдерыИзОтветаКомпоненты(ОтветКомпоненты, ПрограммыПоИменамСТипом, 
	ПроверкаНаКлиенте = Истина) Экспорт
	
	Попытка
		ВсеКриптопровайдеры = ПрочитатьОтветКомпоненты(ОтветКомпоненты);
		Криптопровайдеры = ВсеКриптопровайдеры.Получить("providers");
	Исключение
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при чтении свойств криптопровайдеров:
				 | %1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
	Если ТипЗнч(Криптопровайдеры) <> Тип("Массив") Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	КриптопровайдерыРезультат = Новый Массив;
	Для Каждого ТекущийКриптопровайдер Из Криптопровайдеры Цикл
		
		РасширенноеОписаниеПрограммы = РасширенноеОписаниеПрограммы(
			ТекущийКриптопровайдер, ПрограммыПоИменамСТипом, ПроверкаНаКлиенте);
			
		Если РасширенноеОписаниеПрограммы = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		КриптопровайдерыРезультат.Добавить(РасширенноеОписаниеПрограммы);
		
	КонецЦикла;
	
	Возврат КриптопровайдерыРезультат;
	
КонецФункции

Функция ПрочитатьОтветКомпоненты(Текст) Экспорт
	
	#Если ВебКлиент Тогда
	Возврат ЭлектроннаяПодписьСлужебныйВызовСервера.ПрочитатьОтветКомпоненты(Текст);
	#Иначе
	Попытка
		ЧтениеJSON = Новый ЧтениеJSON;
		ЧтениеJSON.УстановитьСтроку(Текст);
		Результат = ПрочитатьJSON(ЧтениеJSON, Истина);
		ЧтениеJSON.Закрыть();
	Исключение
		
		ИнформацияОбОшибке = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
		
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось прочитать ответ компоненты: %1
					|%2'"), Текст, ИнформацияОбОшибке);
		
	КонецПопытки;
	
	Возврат Результат;
	#КонецЕсли
	
КонецФункции

Функция ОпределитьПрограмму(СвойстваСертификата,
			УстановленныеКриптопровайдеры, ПоискПрограммПоОткрытомуКлючу, ОписаниеОшибки = "") Экспорт
	
	Если УстановленныеКриптопровайдеры.Количество() = 0 Тогда
		ОписаниеОшибки = НСтр("ru = 'Не установлены программы для работы с электронной подписью'");
		Возврат Неопределено;
	КонецЕсли;
	
	ПрограммыПоОткрытомуКлючу = ПоискПрограммПоОткрытомуКлючу.Получить(СвойстваСертификата.АлгоритмОткрытогоКлюча);
	
	Если ПрограммыПоОткрытомуКлючу = Неопределено Тогда
		ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не найдено подходящих программ по открытому ключу сертификата %1'"),
			СвойстваСертификата.АлгоритмОткрытогоКлюча);
		Возврат Неопределено;
	КонецЕсли;
	
	ПрограммаНайдена = Неопределено;
	
	Для Каждого УстановленныйКриптопровайдер Из УстановленныеКриптопровайдеры Цикл
		
		Если ПрограммаНеИспользуется(УстановленныйКриптопровайдер.РежимИспользования) Тогда
			Продолжить;
		КонецЕсли;
		
		Программа = ПрограммыПоОткрытомуКлючу.Получить(
			КлючПоискаПрограммыПоИмениСТипом(УстановленныйКриптопровайдер.ИмяПрограммы, УстановленныйКриптопровайдер.ТипПрограммы));
		Если Программа = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		// Приоритет КриптоПро, если установлено несколько подходящих программ.
		Если СтрНайти(Программа, "CryptoPro") Тогда
			Возврат УстановленныйКриптопровайдер;
		КонецЕсли;
		
		// Приоритет Microsoft Enhanced CSP, если установлено несколько подходящих программ.
		Если СтрНайти(Программа, "MicrosoftEnhanced") Тогда
			Возврат УстановленныйКриптопровайдер;
		КонецЕсли;
		
		ПрограммаНайдена = УстановленныйКриптопровайдер;
		
	КонецЦикла;
	
	Если ПрограммаНайдена = Неопределено Тогда
		
		ИдентификаторыАлгоритмов = ИдентификаторыАлгоритмовПодписи(Истина);
		АлгоритмПодписи = АлгоритмПоOID(СвойстваСертификата.АлгоритмОткрытогоКлюча, ИдентификаторыАлгоритмов, Ложь);
		
		ШаблонОшибки = НСтр("ru = 'Не предусмотрено использование ни одной программы
			|с алгоритмом подписи %1'");
		ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			СокрЛП(СтрРазделить(АлгоритмПодписи, ",")[0]));
			
	КонецЕсли;
	
	Возврат ПрограммаНайдена;
	
КонецФункции

Функция КлючПоискаПрограммыПоИмениСТипом(Имя, Тип) Экспорт
	
	Возврат СтрШаблон("%1 (%2)", Имя, Тип);
	
КонецФункции

//  Возвращаемое значение:
//   Структура - содержит ошибки выполнения операций программами:
//     * ОписаниеОшибки  - Строка - полное описание ошибки, когда оно возвращается строкой.
//     * ЗаголовокОшибки - Строка - заголовок ошибки, который соответствует операции
//                                  когда операция одна (не заполнен, когда операций несколько).
//     * Общая           - Булево - если Истина, то одна ошибка общая для всех программ.
//     * ИмяКомпьютера   - Строка - имя компьютера при выполнении операции на стороне сервера.
//     * Ошибки          - Массив из см. НовыеСвойстваОшибки
//
Функция НовоеОписаниеОшибок(ИмяКомпьютера = "") Экспорт
	
	Описание = Новый Структура;
	Описание.Вставить("ОписаниеОшибки",  "");
	Описание.Вставить("ЗаголовокОшибки", "");
	Описание.Вставить("Общая",           Ложь);
	Описание.Вставить("ИмяКомпьютера",   ИмяКомпьютера);
	Описание.Вставить("Ошибки",          Новый Массив);
	
	Возврат Описание;
	
КонецФункции

// Возвращает свойства ошибки выполнения одной операции одной программой.
//
// Возвращаемое значение:
//  Структура:
//   * ЗаголовокОшибки   - Строка - заголовок ошибки, который соответствует операции
//                           когда операций несколько (не заполнен, когда операция одна).
//   * Описание          - Строка - краткое представление ошибки.
//   * ИзИсключения      - Булево - описание ошибки содержит краткое представление информации об ошибке.
//   * НетРасширения     - Булево - не подключилось расширение для работы с криптографией (требуется установить).
//   * КАдминистратору   - Булево - для исправления ошибки требуются права администратора.
//   * Инструкция        - Булево - для исправления требуется инструкция по работе с программами ЭП.
//   * НастройкаПрограмм - Булево - для исправления ошибки требуется настройка программ.
//   * Программа         - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//                       - Строка - если не
//                           заполнено, значит ошибка, общая для всех программ.
//   * НетАлгоритма      - Булево - менеджер криптографии не поддерживает алгоритм, указанный
//                                  для его создании в дополнение к указанной программе.
//   * НеУказанПуть      - Булево - для программы не указан путь, необходимый для ОС Linux.
//
Функция НовыеСвойстваОшибки() Экспорт
	
	ПустаяПрограмма = ПредопределенноеЗначение("Справочник.ПрограммыЭлектроннойПодписиИШифрования.ПустаяСсылка");
	
	СвойстваОшибки = Новый Структура;
	СвойстваОшибки.Вставить("ЗаголовокОшибки",   "");
	СвойстваОшибки.Вставить("Описание",          "");
	СвойстваОшибки.Вставить("ИзИсключения",      Ложь);
	СвойстваОшибки.Вставить("НеПоддерживается",  Ложь);
	СвойстваОшибки.Вставить("НетРасширения",     Ложь);
	СвойстваОшибки.Вставить("КАдминистратору",   Ложь);
	СвойстваОшибки.Вставить("Инструкция",        Ложь);
	СвойстваОшибки.Вставить("НастройкаПрограмм", Ложь);
	СвойстваОшибки.Вставить("Программа",         ПустаяПрограмма);
	СвойстваОшибки.Вставить("НетАлгоритма",      Ложь);
	СвойстваОшибки.Вставить("НеУказанПуть",      Ложь);
	
	Возврат СвойстваОшибки;
	
КонецФункции

// Текст ошибки вызова метода компоненты ExtraCryptoAPI.
// 
// Параметры:
//  ИмяМетода - Строка
//  ИнформацияОбОшибке - Строка
// 
// Возвращаемое значение:
//  Строка
//
Функция ОшибкаВызоваМетодаКомпоненты(ИмяМетода, ИнформацияОбОшибке) Экспорт
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка вызова метода %1 компоненты %2.'"), ИмяМетода, "ExtraCryptoAPI")
		+ Символы.ПС + ИнформацияОбОшибке;
	
КонецФункции

// Текст ошибки для отозванного сертификата подписи.
// 
// Параметры:
//  РезультатПроверкиПодписи - см. ЭлектроннаяПодписьКлиентСервер.РезультатПроверкиПодписи
// 
// Возвращаемое значение:
//  Строка - текст ошибки для отозванного сертификата подписи
//
Функция ТекстОшибкиДляОтозванногоСертификатаПодписи(РезультатПроверкиПодписи) Экспорт
	
	Если ЗначениеЗаполнено(РезультатПроверкиПодписи.ДатаПодписиИзМетки) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Сертификат отозван. Подпись верна, если сертификат был отозван позднее %1. Чтобы принять решение о том, верна ли подпись, запросите в удостоверяющем центре, выдавшем сертификат, причину и дату отзыва.'"),
			РезультатПроверкиПодписи.ДатаПодписиИзМетки);
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Сертификат отозван. Подпись могла быть верна на дату подписания %1, если сертификат был отозван позднее. Чтобы узнать причину и дату отзыва, обратитесь в удостоверяющий центр, выдавший сертификат.'"),
			РезультатПроверкиПодписи.НеподтвержденнаяДатаПодписи);
	КонецЕсли;
	
	Возврат ТекстОшибки;
	
КонецФункции


// Только для внутреннего использования.
// 
// Параметры:
//  Программа - СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования
//            - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//            - см. НовоеРасширенноеОписаниеПрограммы
//  Ошибки - Массив
//  ОписанияПрограмм - Массив из см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//  ПрограммыАвто - Массив из см. НовоеРасширенноеОписаниеПрограммы
//
// Возвращаемое значение:
//  Массив из см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//
Функция МенеджерКриптографииОписанияПрограмм(Программа, Ошибки, Знач ОписанияПрограмм, Знач ПрограммыАвто = Неопределено) Экспорт
	
	Если ТипЗнч(Программа) = Тип("Структура") Или ТипЗнч(Программа) = Тип("ФиксированнаяСтруктура") Тогда
		
		ОписанияПрограмм = Новый Массив;
		ОписанияПрограмм.Добавить(Программа);
		Возврат ОписанияПрограмм;
		
	ИначеЕсли Программа <> Неопределено Тогда
		
		ПрограммаНайдена = Ложь;
		
		Для Каждого ОписаниеПрограммы Из ОписанияПрограмм Цикл
			
			Если ОписаниеПрограммы.Ссылка = Программа Тогда
				
				Если ИспользуютсяАвтоматическиеНастройки(ОписаниеПрограммы.РежимИспользования)
					И ЗначениеЗаполнено(ПрограммыАвто) Тогда
					
					Для Каждого ПрограммаАвто Из ПрограммыАвто Цикл
						Если ПрограммаАвто.ИмяПрограммы = ОписаниеПрограммы.ИмяПрограммы
							И ПрограммаАвто.ТипПрограммы = ОписаниеПрограммы.ТипПрограммы Тогда
							ОписанияПрограмм = Новый Массив;
							ОписанияПрограмм.Добавить(ПрограммаАвто);
							Возврат ОписанияПрограмм;
						КонецЕсли;
					КонецЦикла;
					
				КонецЕсли;
				
				Если ПрограммаНеИспользуется(ОписаниеПрограммы.РежимИспользования) Тогда
					Прервать;
				КонецЕсли;
				
				ПрограммаНайдена = Истина;
				Прервать;
				
			КонецЕсли;
			
		КонецЦикла;
		
		Если Не ПрограммаНайдена Тогда
			МенеджерКриптографииДобавитьОшибку(Ошибки, Программа,
				НСтр("ru = 'Программа не предусмотрена для использования.'"), Истина);
			Возврат Неопределено;
		КонецЕсли;
		
		ОписанияПрограмм = Новый Массив;
		ОписанияПрограмм.Добавить(ОписаниеПрограммы);
		
	ИначеЕсли ПрограммыАвто <> Неопределено Тогда
		
		Для Каждого ОписаниеПрограммы Из ОписанияПрограмм Цикл
			Если Не ЗначениеЗаполнено(ОписаниеПрограммы.Идентификатор)
				И Не ПрограммаНеИспользуется(ОписаниеПрограммы.РежимИспользования) Тогда
				
				Найдено = Неопределено;
				Для Каждого ПрограммаАвто Из ПрограммыАвто Цикл
					Если ОписаниеПрограммы.ИмяПрограммы = ПрограммаАвто.ИмяПрограммы
						И ОписаниеПрограммы.ТипПрограммы = ПрограммаАвто.ТипПрограммы Тогда
						Найдено = ПрограммаАвто;
						Прервать;
					КонецЕсли;
				КонецЦикла;
				
				Если Найдено = Неопределено Тогда
					НовоеОписание = НовоеРасширенноеОписаниеПрограммы();
					ЗаполнитьЗначенияСвойств(НовоеОписание, ОписаниеПрограммы);
					НовоеОписание.Автоопределение = Ложь;
					ПрограммыАвто.Добавить(НовоеОписание);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		
		Возврат ПрограммыАвто;
		
	КонецЕсли;
	
	Возврат ОписанияПрограмм;
	
КонецФункции

Функция ИспользуютсяАвтоматическиеНастройки(РежимИспользования) Экспорт

	Возврат РежимИспользования = ПредопределенноеЗначение(
		"Перечисление.РежимыИспользованияПрограммыЭлектроннойПодписи.Автоматически")
		
КонецФункции

Функция ПрограммаНеИспользуется(РежимИспользования) Экспорт

	Возврат РежимИспользования = ПредопределенноеЗначение(
		"Перечисление.РежимыИспользованияПрограммыЭлектроннойПодписи.НеИспользуется")
		
КонецФункции

// Только для внутреннего использования.
// 
// Параметры:
//  ОписаниеПрограммы - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//  ЭтоLinux - Булево
//  Ошибки - Массив
//  ЭтоСервер - Булево
//  ПутиКПрограммамНаСерверахLinux - Строка
// 
// Возвращаемое значение:
//  Структура:
//   * ПутьКПрограмме - Строка
//  Неопределено
//
Функция МенеджерКриптографииСвойстваПрограммы(ОписаниеПрограммы, ЭтоLinux, Ошибки, ЭтоСервер,
			ОписаниеПути) Экспорт
	
	Если Не ЗначениеЗаполнено(ОписаниеПрограммы.ИмяПрограммы) Тогда
		МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка,
			НСтр("ru = 'Не указано имя программы.'"), Истина);
		Возврат Неопределено;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ОписаниеПрограммы.ТипПрограммы) Тогда
		МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка,
			НСтр("ru = 'Не указан тип программы.'"), Истина);
		Возврат Неопределено;
	КонецЕсли;
	
	СвойстваПрограммы = Новый Структура("ИмяПрограммы, ПутьКПрограмме, ТипПрограммы");
	
	ПутьКПрограмме = "";
	Автоопределение = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(ОписаниеПрограммы, "Автоопределение", Ложь);
	Если ЭтоLinux И Не Автоопределение Тогда
		Если ЗначениеЗаполнено(ОписаниеПути.ПутьКПрограмме) И Не ОписаниеПути.Существует Тогда
			Если ОписаниеПути.Свойство("ТекстОшибки")
			   И ЗначениеЗаполнено(ОписаниеПути.ТекстОшибки) Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось определить путь к программе по причине:
					           |%1'"), ОписаниеПути.ТекстОшибки);
			Иначе
				ПутиКМодулям = СтрРазделить(ОписаниеПути.ПутьКПрограмме, ":", Ложь);
				Если ПутиКМодулям.Количество() = 1 Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Файл не существует: ""%1"".'"), ПутиКМодулям[0]);
				Иначе
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Ни один из файлов не существует: ""%1"".'"),
						СтрСоединить(ПутиКМодулям, """, """));
				КонецЕсли;
			КонецЕсли;
		Иначе
			ТекстОшибки = "";
			ПутьКПрограмме = ОписаниеПути.ПутьКПрограмме;
		КонецЕсли;
		Если ЗначениеЗаполнено(ТекстОшибки) Тогда
			МенеджерКриптографииДобавитьОшибку(Ошибки,
				ОписаниеПрограммы.Ссылка, ТекстОшибки, ЭтоСервер);
			Возврат Неопределено;
		КонецЕсли;
	КонецЕсли;
	
	СвойстваПрограммы = Новый Структура;
	СвойстваПрограммы.Вставить("ИмяПрограммы",   ОписаниеПрограммы.ИмяПрограммы);
	СвойстваПрограммы.Вставить("ПутьКПрограмме", ПутьКПрограмме);
	СвойстваПрограммы.Вставить("ТипПрограммы",   ОписаниеПрограммы.ТипПрограммы);
	
	Возврат СвойстваПрограммы;
	
КонецФункции

// Только для внутреннего использования.
// Параметры:
//  ОписаниеПрограммы - Структура:
//    * Ссылка - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//  АлгоритмыПодписи - Массив из Строка
//  АлгоритмПодписи - Строка
//  Ошибки - Массив из см. НовыеСвойстваОшибки
//  ЭтоСервер - Булево
//  ДобавлятьОшибку - Булево
// 
// Возвращаемое значение:
//  Булево
//
Функция МенеджерКриптографииАлгоритмПодписиПоддерживается(ОписаниеПрограммы, Операция,
			АлгоритмПодписи, Ошибки, ЭтоСервер, ДобавлятьОшибку) Экспорт
	
	ВозможныеАлгоритмы = СтрРазделить(АлгоритмПодписи, ",", Ложь);
	
	Для Каждого ВозможныйАлгоритм Из ВозможныеАлгоритмы Цикл
		ВозможныйАлгоритм = СокрЛП(ВозможныйАлгоритм);
		
		Если ВРег(ОписаниеПрограммы.АлгоритмПодписи) = ВРег(ВозможныйАлгоритм)
		 Или (Операция = "ПроверкаПодписи" Или Операция = "ПроверкаСертификата" Или Операция = "ПродлениеСрокаДействияПодписи" Или Операция = "Шифрование")
		   И ОписаниеПрограммы.АлгоритмыПроверкиПодписи.Найти(ВозможныйАлгоритм) <> Неопределено Тогда
			
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ДобавлятьОшибку Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Программа не поддерживает алгоритм подписи %1.'"),
		СокрЛП(СтрРазделить(ВозможныеАлгоритмы, ",")[0]));
	
	МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка, ТекстОшибки, ЭтоСервер, Истина);
	Ошибки[Ошибки.ВГраница()].НетАлгоритма = Истина;
	
	Возврат Ложь;
	
КонецФункции

// Только для внутреннего использования.
// 
// Параметры:
//  ОписаниеПрограммы - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//  Менеджер - МенеджерКриптографии
//  Ошибки - Массив
//
// Возвращаемое значение:
//  Булево
//
Функция МенеджерКриптографииАлгоритмыУстановлены(ОписаниеПрограммы, Менеджер, Ошибки) Экспорт
	
	Если ОписаниеПрограммы.ИмяПрограммы = "Default" Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ВыполненОбходНарушенияОбратнойСовместимостиВViPNetCSP_4_4(ОписаниеПрограммы, Менеджер) Тогда
		Возврат Истина;
	КонецЕсли;
	
	АлгоритмПодписи = Строка(ОписаниеПрограммы.АлгоритмПодписи);
	Попытка
		Менеджер.АлгоритмПодписи = АлгоритмПодписи;
	Исключение
		Менеджер = Неопределено;
		// Платформа использует обобщенное сообщение "Неизвестный алгоритм криптографии". Требуется более конкретное.
		МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Выбран неизвестный алгоритм подписи ""%1"".'"), АлгоритмПодписи), Истина);
		Возврат Ложь;
	КонецПопытки;
	
	АлгоритмХеширования = Строка(ОписаниеПрограммы.АлгоритмХеширования);
	Попытка
		Менеджер.АлгоритмХеширования = АлгоритмХеширования;
	Исключение
		Менеджер = Неопределено;
		// Платформа использует обобщенное сообщение "Неизвестный алгоритм криптографии". Требуется более конкретное.
		МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Выбран неизвестный алгоритм хеширования ""%1"".'"), АлгоритмХеширования), Истина);
		Возврат Ложь;
	КонецПопытки;
	
	АлгоритмШифрования = Строка(ОписаниеПрограммы.АлгоритмШифрования);
	Попытка
		Менеджер.АлгоритмШифрования = АлгоритмШифрования;
	Исключение
		Менеджер = Неопределено;
		// Платформа использует обобщенное сообщение "Неизвестный алгоритм криптографии". Требуется более конкретное.
		МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Выбран неизвестный алгоритм шифрования ""%1"".'"), АлгоритмШифрования), Истина);
		Возврат Ложь;
	КонецПопытки;
	
	Возврат Истина;
	
КонецФункции

// Для функции МенеджерКриптографииАлгоритмыУстановлены.
Функция ВыполненОбходНарушенияОбратнойСовместимостиВViPNetCSP_4_4(ОписаниеПрограммы, Менеджер)
	
	АлгоритмПодписи     = Строка(ОписаниеПрограммы.АлгоритмПодписи);
	АлгоритмХеширования = Строка(ОписаниеПрограммы.АлгоритмХеширования);
	АлгоритмШифрования  = Строка(ОписаниеПрограммы.АлгоритмШифрования);

#Если ВебКлиент Тогда
	
	Если ИменаАлгоритмовПодписиГОСТ_34_10_2012_256().Найти(АлгоритмПодписи) <> Неопределено Тогда
		АлгоритмПодписи = "1.2.643.7.1.1.1.1"; // ГОСТ 34.10-2012 256
	ИначеЕсли ИменаАлгоритмовПодписиГОСТ_34_10_2012_512().Найти(АлгоритмПодписи) <> Неопределено Тогда
		АлгоритмПодписи = "1.2.643.7.1.1.1.2"; // ГОСТ 34.10-2012 512
	КонецЕсли;

	Если ИменаАлгоритмовХешированияГОСТ_34_11_2012_256().Найти(АлгоритмХеширования) <> Неопределено Тогда
		АлгоритмХеширования = "1.2.643.7.1.1.2.2"; // ГОСТ 34.11-2012 256
	ИначеЕсли ИменаАлгоритмовХешированияГОСТ_34_11_2012_512().Найти(АлгоритмХеширования) <> Неопределено Тогда
		АлгоритмХеширования = "1.2.643.7.1.1.2.3"; // ГОСТ 34.11-2012 512
	КонецЕсли;

#Иначе
		Если Не (ОписаниеПрограммы.ИмяПрограммы = "Infotecs GOST 2012/512 Cryptographic Service Provider"
			И ОписаниеПрограммы.ТипПрограммы = 77) И Не (ОписаниеПрограммы.ИмяПрограммы = "Infotecs GOST 2012/1024 Cryptographic Service Provider"
			И ОписаниеПрограммы.ТипПрограммы = 78) Тогда
			Возврат Ложь;
		КонецЕсли;
#КонецЕсли

	АлгоритмыУстановлены = Истина;
	Попытка
		Менеджер.АлгоритмПодписи     = АлгоритмПодписи;
		Менеджер.АлгоритмХеширования = АлгоритмХеширования;
		Менеджер.АлгоритмШифрования  = АлгоритмШифрования;
	Исключение
		АлгоритмыУстановлены = Ложь;
	КонецПопытки;

	Если АлгоритмыУстановлены Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если АлгоритмПодписи     = "GOST 34.10-2012 256"
	   И АлгоритмХеширования = "GOST 34.11-2012 256"
	   И АлгоритмШифрования  = "GOST 28147-89" Тогда
		
		АлгоритмПодписи     = "GR 34.10-2012 256";
		АлгоритмХеширования = "GR 34.11-2012 256";
		АлгоритмШифрования  = "GOST 28147-89";
		
	ИначеЕсли АлгоритмПодписи     = "GR 34.10-2012 256"
	        И АлгоритмХеширования = "GR 34.11-2012 256"
	        И АлгоритмШифрования  = "GOST 28147-89" Тогда
	
		АлгоритмПодписи     = "GOST 34.10-2012 256";
		АлгоритмХеширования = "GOST 34.11-2012 256";
		АлгоритмШифрования  = "GOST 28147-89";
		
	ИначеЕсли АлгоритмПодписи     = "GOST 34.10-2012 512"
	        И АлгоритмХеширования = "GOST 34.11-2012 512"
	        И АлгоритмШифрования  = "GOST 28147-89" Тогда
		
		АлгоритмПодписи     = "GR 34.10-2012 512";
		АлгоритмХеширования = "GR 34.11-2012 512";
		АлгоритмШифрования  = "GOST 28147-89";
		
	ИначеЕсли АлгоритмПодписи     = "GR 34.10-2012 512"
	        И АлгоритмХеширования = "GR 34.11-2012 512"
	        И АлгоритмШифрования  = "GOST 28147-89" Тогда
	
		АлгоритмПодписи     = "GOST 34.10-2012 512";
		АлгоритмХеширования = "GOST 34.11-2012 512";
		АлгоритмШифрования  = "GOST 28147-89";
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
	АлгоритмыУстановлены = Истина;
	Попытка
		Менеджер.АлгоритмПодписи     = АлгоритмПодписи;
		Менеджер.АлгоритмХеширования = АлгоритмХеширования;
		Менеджер.АлгоритмШифрования  = АлгоритмШифрования;
	Исключение
		АлгоритмыУстановлены = Ложь;
	КонецПопытки;
	
	Возврат АлгоритмыУстановлены;
	
КонецФункции

// Только для внутреннего использования.
// 
// Параметры:
//  ОписаниеПрограммы - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//  Ошибки - Массив
//  ЭтоСервер - Булево
//
Процедура МенеджерКриптографииПрограммаНеНайдена(ОписаниеПрограммы, Ошибки, ЭтоСервер) Экспорт
	
	МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка,
		НСтр("ru = 'Программа не установлена на компьютере.'"), ЭтоСервер, Истина);
	
КонецПроцедуры

// Только для внутреннего использования.
// 
// Параметры:
//  ОписаниеПрограммы - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//  ИмяПрограммыПолученное - Строка
//  Ошибки - Массив
//  ЭтоСервер - Булево
//
// Возвращаемое значение:
//  Булево
//
Функция МенеджерКриптографииИмяПрограммыСовпадает(ОписаниеПрограммы, ИмяПрограммыПолученное, Ошибки, ЭтоСервер) Экспорт
	
	Если ИмяПрограммыПолученное <> ОписаниеПрограммы.ИмяПрограммы Тогда
		МенеджерКриптографииДобавитьОшибку(Ошибки, ОписаниеПрограммы.Ссылка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Получена другая программа с именем ""%1"".'"), ИмяПрограммыПолученное), ЭтоСервер, Истина);
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Только для внутреннего использования.
//
// Параметры:
//  Ошибки    - Массив из см. НовыеСвойстваОшибки
//  Программа - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//            - Структура - см. НовоеРасширенноеОписаниеПрограммы
//  Описание  - Строка
//  КАдминистратору - Булево
//  Инструкция   - Булево
//  ИзИсключения - Булево
//  НеУказанПуть - Булево
//
Процедура МенеджерКриптографииДобавитьОшибку(Ошибки, Программа, Описание,
			КАдминистратору, Инструкция = Ложь, ИзИсключения = Ложь, НеУказанПуть = Ложь) Экспорт
	
	СвойстваОшибки = НовыеСвойстваОшибки();
	Если ТипЗнч(Программа) = Тип("СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования") Тогда
		Если ЗначениеЗаполнено(Программа) Тогда
			СвойстваОшибки.Программа = Программа;
		КонецЕсли;
	ИначеЕсли ТипЗнч(Программа) = Тип("Структура") Тогда
		Если ЗначениеЗаполнено(Программа.Ссылка) Тогда
			СвойстваОшибки.Программа = Программа.Ссылка;
		Иначе
			СвойстваОшибки.Программа = ?(ЗначениеЗаполнено(Программа.Представление), Программа.Представление,
				КлючПоискаПрограммыПоИмениСТипом(Программа.ИмяПрограммы, Программа.ТипПрограммы));
		КонецЕсли;
	КонецЕсли;
	СвойстваОшибки.Описание          = Описание;
	СвойстваОшибки.КАдминистратору   = КАдминистратору;
	СвойстваОшибки.Инструкция        = Инструкция;
	СвойстваОшибки.ИзИсключения      = ИзИсключения;
	СвойстваОшибки.НеУказанПуть      = НеУказанПуть;
	СвойстваОшибки.НастройкаПрограмм = Истина;
	
	Ошибки.Добавить(СвойстваОшибки);
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//  ОписаниеОшибок - см. НовоеОписаниеОшибок
//  Программа - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//  АлгоритмПодписи - Строка
//  ЭтоПолноправныйПользователь - Булево
//  ЭтоСервер - Булево
//
Процедура МенеджерКриптографииЗаполнитьПредставлениеОшибок(ОписаниеОшибок,
			Программа, АлгоритмПодписи, ЭтоПолноправныйПользователь, ЭтоСервер) Экспорт
		
	Если ОписаниеОшибок.Ошибки.Количество() = 0 Тогда
		Если Не ЗначениеЗаполнено(АлгоритмПодписи) Тогда
			ТекстОшибки = НСтр("ru = 'Не предусмотрено использование ни одной программы.'");
		Иначе
			ШаблонОшибки = НСтр("ru = 'Не предусмотрено использование ни одной программы
			                          |с алгоритмом подписи %1.'");
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
				СокрЛП(СтрРазделить(АлгоритмПодписи, ",")[0]));
		КонецЕсли;
		ОписаниеОшибок.Общая = Истина;
		МенеджерКриптографииДобавитьОшибку(ОписаниеОшибок.Ошибки,
			Неопределено, ТекстОшибки, Истина, Истина);
	КонецЕсли;
	
	ЗаполнитьОбщееПредставлениеОшибок(ОписаниеОшибок, ЭтоПолноправныйПользователь);
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//  ОписаниеОшибок - см. НовоеОписаниеОшибок
//  ЭтоПолноправныйПользователь - Булево
//
Процедура ЗаполнитьОбщееПредставлениеОшибок(ОписаниеОшибок, ЭтоПолноправныйПользователь)
	
	ЧастиОписания = Новый Массив;
	Если ЗначениеЗаполнено(ОписаниеОшибок.ЗаголовокОшибки) Тогда
		ЧастиОписания.Добавить(ОписаниеОшибок.ЗаголовокОшибки);
	КонецЕсли;
	
	КАдминистратору = Ложь;
	Для Каждого СвойстваОшибки Из ОписаниеОшибок.Ошибки Цикл
		Описание = "";
		Если ЗначениеЗаполнено(СвойстваОшибки.ЗаголовокОшибки) Тогда
			Описание = Описание + СвойстваОшибки.ЗаголовокОшибки + Символы.ПС;
		КонецЕсли;
		Если ЗначениеЗаполнено(СвойстваОшибки.Программа) Тогда
			Описание = Описание + Строка(СвойстваОшибки.Программа) + ":" + Символы.ПС;
		КонецЕсли;
		ЧастиОписания.Добавить(Описание + СвойстваОшибки.Описание);
		КАдминистратору = КАдминистратору Или СвойстваОшибки.КАдминистратору;
	КонецЦикла;
	ОписаниеОшибки = СтрСоединить(ЧастиОписания, Символы.ПС);
	
	Если КАдминистратору И Не ЭтоПолноправныйПользователь Тогда
		ОписаниеОшибки = ОписаниеОшибки + Символы.ПС + Символы.ПС
			+ НСтр("ru = 'Обратитесь к администратору.'");
	КонецЕсли;
	
	ОписаниеОшибок.ОписаниеОшибки = ОписаниеОшибки;
	
КонецПроцедуры

// Параметры:
//  ЗаголовокОшибки - Строка
//  ОписаниеОшибок - см. НовоеОписаниеОшибок
//
Функция ТекстОшибкиПоискаПрограммы(Знач ЗаголовокОшибки, ОписаниеОшибок) Экспорт
	
	Для Каждого Ошибка Из ОписаниеОшибок.Ошибки Цикл
		Прервать;
	КонецЦикла;
	
	ЗаголовокОшибки = СтрЗаменить(ЗаголовокОшибки, "%1", ОписаниеОшибок.ИмяКомпьютера);
	Возврат ЗаголовокОшибки + " " + Ошибка.Описание;
	
КонецФункции

// Только для внутреннего использования.
//
// Параметры:
//  Контекст - Структура:
//   * ОписаниеПрограммы - Структура:
//      * Ссылка - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//  Ошибка - см. НовоеОписаниеОшибок
//
// Возвращаемое значение:
//  СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования
//
Функция ЗаписатьСертификатВСправочник(Контекст, Ошибка) Экспорт
	
	Контекст.ДополнительныеПараметры.Программа = Контекст.ОписаниеПрограммы.Ссылка;
	Попытка
		Сертификат = ЭлектроннаяПодписьСлужебныйВызовСервера.ЗаписатьСертификатВСправочник(
			Контекст.ДанныеСертификата, Контекст.ДополнительныеПараметры);
	Исключение
		Сертификат = Неопределено;
		Контекст.ЗаголовокФормы = НСтр("ru = 'Ошибка добавления сертификата'");
		
		Ошибка.Общая = Истина;
		Ошибка.ЗаголовокОшибки = НСтр("ru = 'Не удалось записать сертификат по причине:'");
		
		СвойстваОшибки = НовыеСвойстваОшибки();
		СвойстваОшибки.Описание = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		Ошибка.Ошибки.Добавить(СвойстваОшибки);
	КонецПопытки;
	
	Возврат Сертификат;
	
КонецФункции

// Только для внутреннего использования.
Функция ЗаголовокОшибкиДобавленияСертификата(Операция, ИмяКомпьютера = "") Экспорт
	
	Если ЗначениеЗаполнено(ИмяКомпьютера) Тогда // Признак ЭтоСервер.
		Если Операция = "Подписание" Тогда
			ШаблонЗаголовка = НСтр("ru = 'Не удалось пройти проверку подписания на сервере %1 по причине:'");
		ИначеЕсли Операция = "Шифрование" Тогда
			ШаблонЗаголовка = НСтр("ru = 'Не удалось пройти проверку шифрования на сервере %1 по причине:'");
		ИначеЕсли Операция = "Расшифровка" Тогда
			ШаблонЗаголовка = НСтр("ru = 'Не удалось пройти проверку расшифровки на сервере %1 по причине:'");
		КонецЕсли;
		ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ШаблонЗаголовка, ИмяКомпьютера);
	Иначе
		Если Операция = "Подписание" Тогда
			ЗаголовокОшибки = НСтр("ru = 'Не удалось пройти проверку подписания на компьютере по причине:'");
		ИначеЕсли Операция = "Шифрование" Тогда
			ЗаголовокОшибки = НСтр("ru = 'Не удалось пройти проверку шифрования на компьютере по причине:'");
		ИначеЕсли Операция = "Расшифровка" Тогда
			ЗаголовокОшибки = НСтр("ru = 'Не удалось пройти проверку расшифровки на компьютере по причине:'");
		КонецЕсли;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ЗаголовокОшибки) Тогда
		ТекущийТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректное значение параметра Операция ""%1"" в процедуре %2'"),
			Операция,
			"ЗаполнитьОшибкуДобавленияСертификата");
		ВызватьИсключение ТекущийТекстОшибки;
	КонецЕсли;
	
	Возврат ЗаголовокОшибки;
	
КонецФункции

// Только для внутреннего использования.
//
// Параметры:
//  ОписаниеОшибок - см. НовоеОписаниеОшибок
//  ОписаниеПрограммы - Структура:
//   * Ссылка - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//  Операция - Строка
//  ТекстОшибки - Строка
//  ЭтоПолноправныйПользователь - Булево
//  ПустыеДанные - Булево
//  ИмяКомпьютера - Строка
//
Процедура ЗаполнитьОшибкуДобавленияСертификата(ОписаниеОшибок, ОписаниеПрограммы, Операция,
			ТекстОшибки, ЭтоПолноправныйПользователь, ПустыеДанные = Ложь, ИмяКомпьютера = "") Экспорт
	
	ЗаголовокОшибки = ЗаголовокОшибкиДобавленияСертификата(Операция, ИмяКомпьютера);
	
	СвойстваОшибки = НовыеСвойстваОшибки();
	СвойстваОшибки.Описание = ТекстОшибки;
	СвойстваОшибки.Программа = ОписаниеПрограммы.Ссылка;
	
	Если Не ПустыеДанные Тогда
		СвойстваОшибки.ИзИсключения = Истина;
		СвойстваОшибки.Инструкция = Истина;
		СвойстваОшибки.НастройкаПрограмм = Истина;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ОписаниеОшибок.Ошибки) Тогда
		ОписаниеОшибок.ЗаголовокОшибки = ЗаголовокОшибки;
		
	ИначеЕсли Не ЗначениеЗаполнено(ОписаниеОшибок.ЗаголовокОшибки) Тогда
		СвойстваОшибки.ЗаголовокОшибки = ЗаголовокОшибки;
		
	ИначеЕсли ОписаниеОшибок.ЗаголовокОшибки <> ЗаголовокОшибки Тогда
		Для Каждого ТекущиеСвойства Из ОписаниеОшибок.Ошибки Цикл
			ТекущиеСвойства.ЗаголовокОшибки = ОписаниеОшибок.ЗаголовокОшибки;
		КонецЦикла;
		ОписаниеОшибок.ЗаголовокОшибки = "";
		СвойстваОшибки.ЗаголовокОшибки = ЗаголовокОшибки;
	КонецЕсли;
	
	ОписаниеОшибок.Ошибки.Добавить(СвойстваОшибки);
	
	ЗаполнитьОбщееПредставлениеОшибок(ОписаниеОшибок, ЭтоПолноправныйПользователь);
	
КонецПроцедуры

// Только для внутреннего использования.
Функция РежимыПроверкиСертификата(ИгнорироватьВремяДействия = Ложь) Экспорт
	
	МассивРежимовПроверки = Новый Массив;
	
	#Если ВебКлиент Тогда
		МассивРежимовПроверки.Добавить(РежимПроверкиСертификатаКриптографии.РазрешитьТестовыеСертификаты);
	#КонецЕсли
	
	Если ИгнорироватьВремяДействия Тогда
		МассивРежимовПроверки.Добавить(РежимПроверкиСертификатаКриптографии.ИгнорироватьВремяДействия);
	КонецЕсли;
	
	Возврат МассивРежимовПроверки;
	
КонецФункции

// Только для внутреннего использования.
Функция ПараметрыПроверкиСертификатаВСервисе(ОбщиеНастройки, РежимыПроверкиСертификата) Экспорт
	
	Если Не ОбщиеНастройки.ДоступнаПроверкаСертификатаВОблачномСервисеСПараметрами Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Режимы = Новый Массив;
	Для Каждого Режим Из РежимыПроверкиСертификата Цикл
		Если Режим = РежимПроверкиСертификатаКриптографии.ИгнорироватьВремяДействия Тогда
			Режимы.Добавить("ИгнорироватьВремяДействия");
		ИначеЕсли Режим = РежимПроверкиСертификатаКриптографии.ИгнорироватьДействительностьПодписи Тогда
			Режимы.Добавить("ИгнорироватьДействительностьПодписи");
		ИначеЕсли Режим = РежимПроверкиСертификатаКриптографии.ИгнорироватьПроверкуВСпискеОтозванныхСертификатов Тогда
			Режимы.Добавить("ИгнорироватьПроверкуВСпискеОтозванныхСертификатов");
		ИначеЕсли Режим = РежимПроверкиСертификатаКриптографии.РазрешитьТестовыеСертификаты Тогда
			Режимы.Добавить("РазрешитьТестовыеСертификаты");
		КонецЕсли;
	КонецЦикла;
	
	Возврат Новый Структура("РежимПроверкиСертификата", СтрСоединить(Режимы, ","));
	
КонецФункции

// Только для внутреннего использования.
Функция СертификатПросрочен(Сертификат, НаДату, ДобавкаВремени) Экспорт
	
	Если Не ЗначениеЗаполнено(НаДату) Тогда
		Возврат "";
	КонецЕсли;
	
	ДатыСертификата = ДатыСертификата(Сертификат, ДобавкаВремени);
	
	Если ДатыСертификата.ДатаОкончания > НачалоДня(НаДату) Тогда
		Возврат "";
	КонецЕсли;
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'На %1 просрочен сертификат.'"), Формат(НачалоДня(НаДату), "ДЛФ=D"));
	
КонецФункции

// Только для внутреннего использования.
Функция ЗакрытыйКлючПросрочен(СвойстваСертификата, НаДату) Экспорт
	
	Если Не ЗначениеЗаполнено(НаДату) Или Не ЗначениеЗаполнено(СвойстваСертификата.ДатаОкончанияЗакрытогоКлюча) Тогда
		Возврат "";
	КонецЕсли;
	
	Если СвойстваСертификата.ДатаОкончанияЗакрытогоКлюча > НачалоДня(НаДату) Тогда
		Возврат "";
	КонецЕсли;
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'На %1 просрочен закрытый ключ сертификата.'"), Формат(НачалоДня(НаДату), "ДЛФ=D"));
	
КонецФункции

// Только для внутреннего использования.
Функция ТекстОшибкиСервисаСертификатНедействителен() Экспорт
	
	Возврат НСтр("ru = 'Сервис сообщил, что сертификат недействителен.'");
	
КонецФункции

// Только для внутреннего использования.
//
// Возвращаемое значение:
//  Строка
//
Функция ТекстОшибкиСервисаПодписьНедействительна() Экспорт
	
	Возврат НСтр("ru = 'Сервис сообщил, что подпись недействительна.'");
	
КонецФункции

// Только для внутреннего использования.
Функция ТипХранилищаДляПоискаСертификата(ТолькоВЛичномХранилище) Экспорт
	
	Если ТипЗнч(ТолькоВЛичномХранилище) = Тип("ТипХранилищаСертификатовКриптографии") Тогда
		ТипХранилища = ТолькоВЛичномХранилище;
	ИначеЕсли ТолькоВЛичномХранилище Тогда
		ТипХранилища = ТипХранилищаСертификатовКриптографии.ПерсональныеСертификаты;
	Иначе
		ТипХранилища = Неопределено; // Хранилище, содержащее сертификаты всех доступных типов.
	КонецЕсли;
	
	Возврат ТипХранилища;
	
КонецФункции

// Только для внутреннего использования.
Процедура ДобавитьСвойстваСертификатов(Таблица, МассивСертификатов, БезОтбора,
	ДобавкаВремени, ТекущаяДатаСеанса, Параметры = Неопределено) Экспорт
	
	ТолькоОтпечатки = Ложь;
	ВОблачномСервисе = Ложь;
	ОблачнаяПодпись = Ложь;
	
	Если Параметры <> Неопределено Тогда
		Если Параметры.Свойство("ТолькоОтпечатки") Тогда
			ТолькоОтпечатки = Параметры.ТолькоОтпечатки;
		КонецЕсли;
		Если Параметры.Свойство("ВОблачномСервисе") Тогда
			ВОблачномСервисе = Параметры.ВОблачномСервисе;
		КонецЕсли;
		Если Параметры.Свойство("ОблачнаяПодпись") Тогда
			ОблачнаяПодпись = Параметры.ОблачнаяПодпись;
		КонецЕсли;
	КонецЕсли;
	
	Если ТолькоОтпечатки Тогда
		ОтпечаткиУжеДобавленныхСертификатов = Таблица;
		НаСервере = Ложь;
	Иначе
		ОтпечаткиУжеДобавленныхСертификатов = Новый Соответствие; // Для пропуска дублей.
		НаСервере = ТипЗнч(Таблица) <> Тип("Массив");
	КонецЕсли;
	
	Для Каждого ТекущийСертификат Из МассивСертификатов Цикл
		Отпечаток = Base64Строка(ТекущийСертификат.Отпечаток);
		ДатыСертификата = ДатыСертификата(ТекущийСертификат, ДобавкаВремени);
		
		Если ДатыСертификата.ДатаОкончания <= ТекущаяДатаСеанса Тогда
			Если Не БезОтбора Тогда
				Продолжить; // Пропуск просроченных сертификатов.
			КонецЕсли;
		КонецЕсли;
		
		Если ОтпечаткиУжеДобавленныхСертификатов.Получить(Отпечаток) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ОтпечаткиУжеДобавленныхСертификатов.Вставить(Отпечаток, Истина);
		
		Если ТолькоОтпечатки Тогда
			Продолжить;
		КонецЕсли;
		
		ТипРазмещения = 1;
		Если НаСервере Тогда
			Если ОблачнаяПодпись Тогда
				ТипРазмещения = 3;
			ИначеЕсли ВОблачномСервисе Тогда
				ТипРазмещения = 4;
			Иначе
				ТипРазмещения = 2;
			КонецЕсли;
			Строка = Таблица.Найти(Отпечаток, "Отпечаток");
			Если Строка <> Неопределено Тогда
				Если ВОблачномСервисе Тогда
					Строка.ВОблачномСервисе = Истина;
				КонецЕсли;
				Продолжить; // Пропуск уже добавленных на клиенте.
			КонецЕсли;
		КонецЕсли;
		
		СостояниеСертификата = 2;
		Если ДатыСертификата.ДатаОкончания <= ТекущаяДатаСеанса Тогда
			СостояниеСертификата = 4;
		ИначеЕсли ДатыСертификата.ДатаОкончания <= ТекущаяДатаСеанса + 30*24*60*60 Тогда
			СостояниеСертификата = 3;
		КонецЕсли;
		
		СвойстваСертификата = Новый Структура;
		СвойстваСертификата.Вставить("Отпечаток", Отпечаток);
		СвойстваСертификата.Вставить("Представление",
			ПредставлениеСертификата(ТекущийСертификат, ДобавкаВремени));
		СвойстваСертификата.Вставить("КемВыдан", ПредставлениеИздателя(ТекущийСертификат));
		СвойстваСертификата.Вставить("ТипРазмещения", ТипРазмещения);
		СвойстваСертификата.Вставить("СостояниеСертификата", СостояниеСертификата);
		
		
		Если ТипЗнч(Таблица) = Тип("Массив") Тогда
			Таблица.Добавить(СвойстваСертификата);
		Иначе
			Если ОблачнаяПодпись Тогда
				СвойстваСертификата.Вставить("НаСервере", Ложь);
			ИначеЕсли ВОблачномСервисе Тогда
				СвойстваСертификата.Вставить("ВОблачномСервисе", Истина);
			ИначеЕсли НаСервере Тогда
				СвойстваСертификата.Вставить("НаСервере", Истина);
			КонецЕсли;
			ЗаполнитьЗначенияСвойств(Таблица.Добавить(), СвойстваСертификата);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//   Массив - Массив
//
Процедура ДобавитьОтпечаткиСертификатов(Массив, МассивСертификатов, ДобавкаВремени, ТекущаяДатаСеанса) Экспорт
	
	Для Каждого ТекущийСертификат Из МассивСертификатов Цикл
		Отпечаток = Base64Строка(ТекущийСертификат.Отпечаток);
		Если ТипЗнч(ТекущаяДатаСеанса) = Тип("Дата") Тогда
			ДатыСертификата = ДатыСертификата(ТекущийСертификат, ДобавкаВремени);
			
			Если ДатыСертификата.ДатаОкончания <= ТекущаяДатаСеанса Тогда
				Продолжить; // Пропуск просроченных сертификатов.
			КонецЕсли;
		КонецЕсли;
		Если Массив.Найти(Отпечаток) = Неопределено Тогда
			Массив.Добавить(Отпечаток);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Только для внутреннего использования.
// 
// Параметры:
//  ДвоичныеДанныеПодписи - ДвоичныеДанные, Строка - строка, если передан конверт XML
//  СвойстваСертификата - см. ЭлектроннаяПодписьКлиент.СвойстваСертификата
//  Комментарий - Строка
//  АвторизованныйПользователь - СправочникСсылка.Пользователи
//  ИмяФайлаПодписи - Строка - имя файла подписи
//  ПараметрыПодписи - см. ПараметрыПодписиКриптографии
//  
// Возвращаемое значение:
//  Структура:
//   * Подпись - ДвоичныеДанные
//   * УстановившийПодпись - СправочникСсылка.Пользователи
//   * Комментарий - Строка
//   * ИмяФайлаПодписи - Строка 
//   * ДатаПодписи - Дата - дата подписи неподтвержденная
//   * ДатаПроверкиПодписи - Дата
//   * ПодписьВерна - Булево
//   * Сертификат - ДвоичныеДанные
//   * Отпечаток - Строка
//   * КомуВыданСертификат - Строка
//   * ТипПодписи - ПеречислениеСсылка.ТипыПодписиКриптографии
//   * СрокДействияПоследнейМеткиВремени - Дата
//   * ДатаПодписиИзМетки - Дата 
//   * НеподтвержденнаяДатаПодписи - Дата
//
Функция СвойстваПодписи(ДвоичныеДанныеПодписи, СвойстваСертификата, Комментарий,
			АвторизованныйПользователь, ИмяФайлаПодписи = "", ПараметрыПодписи = Неопределено, ТребуетсяПроверка = Ложь) Экспорт
	
	СвойстваПодписи = Новый Структура;
	СвойстваПодписи.Вставить("Подпись",             ДвоичныеДанныеПодписи);
	СвойстваПодписи.Вставить("УстановившийПодпись", АвторизованныйПользователь);
	СвойстваПодписи.Вставить("Комментарий",         Комментарий);
	СвойстваПодписи.Вставить("ИмяФайлаПодписи",     ИмяФайлаПодписи);
	СвойстваПодписи.Вставить("ДатаПодписи",         Дата('00010101')); // Устанавливается перед записью.
	СвойстваПодписи.Вставить("ДатаПроверкиПодписи", Дата('00010101')); // Дата последней проверки подписи.
	СвойстваПодписи.Вставить("ПодписьВерна",        Ложь);             // Результат последней проверки подписи.
	// Производные свойства:
	СвойстваПодписи.Вставить("Сертификат",          СвойстваСертификата.ДвоичныеДанные);
	СвойстваПодписи.Вставить("Отпечаток",           СвойстваСертификата.Отпечаток);
	СвойстваПодписи.Вставить("КомуВыданСертификат", СвойстваСертификата.КомуВыдан);
	
	СвойстваПодписи.Вставить("ТипПодписи");
	СвойстваПодписи.Вставить("СрокДействияПоследнейМеткиВремени");
	СвойстваПодписи.Вставить("ДатаПодписиИзМетки");
	СвойстваПодписи.Вставить("НеподтвержденнаяДатаПодписи");
	СвойстваПодписи.Вставить("ТребуетсяПроверка", ТребуетсяПроверка);
	СвойстваПодписи.Вставить("ИдентификаторПодписи");
	
	Если ПараметрыПодписи <> Неопределено Тогда
		СвойстваПодписи.Вставить("ТипПодписи", ПараметрыПодписи.ТипПодписи);
		СвойстваПодписи.Вставить("СрокДействияПоследнейМеткиВремени", ПараметрыПодписи.СрокДействияПоследнейМеткиВремени);
		СвойстваПодписи.Вставить("ДатаПодписиИзМетки", ПараметрыПодписи.ДатаПодписиИзМетки);
		СвойстваПодписи.Вставить("НеподтвержденнаяДатаПодписи", ПараметрыПодписи.НеподтвержденнаяДатаПодписи);
	КонецЕсли;
	
	Возврат СвойстваПодписи;
	
КонецФункции

// Только для внутреннего использования.
// Возвращаемое значение:
//  Дата, Неопределено - дата для проверки сертификата подписи
//
Функция ДатаДляПроверкиСертификатаПодписи(ПараметрыПодписи) Экспорт
	
	Если ЗначениеЗаполнено(ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(
		ПараметрыПодписи, "ДатаПодписиИзМетки", Неопределено)) Тогда
		Возврат ПараметрыПодписи.ДатаПодписиИзМетки;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(
		ПараметрыПодписи, "НеподтвержденнаяДатаПодписи", Неопределено)) Тогда
		Возврат ПараметрыПодписи.НеподтвержденнаяДатаПодписи;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

Функция НовыеПараметрыПодписиКриптографии() Экспорт
	
	ПараметрыПодписи = Новый Структура;
	
	ПараметрыПодписи.Вставить("ТипПодписи");
	ПараметрыПодписи.Вставить("СрокДействияПоследнейМеткиВремени");
	ПараметрыПодписи.Вставить("СертификатПоследнейМеткиВремени");
	ПараметрыПодписи.Вставить("ДатаПодписиИзМетки");
	ПараметрыПодписи.Вставить("НеподтвержденнаяДатаПодписи");
	ПараметрыПодписи.Вставить("ДатаПоследнейМеткиВремени");
	ПараметрыПодписи.Вставить("ОписаниеСертификата");
	ПараметрыПодписи.Вставить("Сертификат");
	
	Возврат ПараметрыПодписи;
	
КонецФункции

// Только для внутреннего использования.
//
// Возвращаемое значение:
//  Структура - параметры подписи криптографии:
//   * ТипПодписи          - ПеречислениеСсылка.ТипыПодписиКриптографии
//   * СрокДействияПоследнейМеткиВремени - Дата, Неопределено - заполняется только с помощью менеджера криптографии.
//   * ДатаПодписиИзМетки - Дата, Неопределено - самый ранний штамп времени.
//   * НеподтвержденнаяДатаПодписи - Дата - неподтвержденная дата подписи.
//                                 - Неопределено - неподтвержденная дата подписи отсутствует в данных подписи.
//   * ДатаПоследнейМеткиВремени - Дата - дата последней метки времени.
//   * Сертификат   - СертификатКриптографии - сертификат подписанта.
//   * ОписаниеСертификата - см. ЭлектроннаяПодписьКлиент.СвойстваСертификата.
//
Функция ПараметрыПодписиКриптографии(ПараметрыПодписи, Подпись, СертификатСуществует, ДобавкаВремени, ДатаСеанса) Экспорт
	
	ДатаПодписиИзМетки = Дата(3999, 12, 31);
	
	Если ЗначениеЗаполнено(Подпись.НеподтвержденноеВремяПодписи) Тогда
		ПараметрыПодписи.НеподтвержденнаяДатаПодписи = Подпись.НеподтвержденноеВремяПодписи + ДобавкаВремени;
	КонецЕсли;
	
	ПараметрыПодписи.ТипПодписи = ТипПодписиКриптографии(Подпись.ТипПодписи);
	СрокДействияПоследнейМеткиВремени = Неопределено;
	Если Подпись.МеткаВремениПодписи <> Неопределено Тогда
		СертификатПоследнейМеткиВремени = Подпись.МеткаВремениПодписи.Подписи[0].СертификатПодписи; // СертификатКриптографии
		СрокДействияПоследнейМеткиВремени = СертификатПоследнейМеткиВремени.ДатаОкончания;
		ДатаПодписиИзМетки = Мин(ДатаПодписиИзМетки, Подпись.МеткаВремениПодписи.Дата + ДобавкаВремени);
		ПараметрыПодписи.ДатаПоследнейМеткиВремени = Подпись.МеткаВремениПодписи.Дата + ДобавкаВремени;
	КонецЕсли;
	
	Если Подпись.МеткаВремениДанныхПроверкиПодписи <> Неопределено Тогда
		СертификатПоследнейМеткиВремени = Подпись.МеткаВремениДанныхПроверкиПодписи.Подписи[0].СертификатПодписи;  // СертификатКриптографии
		СрокДействияПоследнейМеткиВремени = СертификатПоследнейМеткиВремени.ДатаОкончания;
		ДатаПодписиИзМетки = Мин(ДатаПодписиИзМетки, Подпись.МеткаВремениДанныхПроверкиПодписи.Дата + ДобавкаВремени);
		ПараметрыПодписи.ДатаПоследнейМеткиВремени = Подпись.МеткаВремениДанныхПроверкиПодписи.Дата + ДобавкаВремени;
	КонецЕсли;
	
	Если Подпись.АрхивныеМеткиВремени.Количество() > 0 Тогда
		ИндексПоследнейМетки = Подпись.АрхивныеМеткиВремени.ВГраница();
		СертификатПоследнейМеткиВремени = Подпись.АрхивныеМеткиВремени[ИндексПоследнейМетки].Подписи[0].СертификатПодписи; // СертификатКриптографии
		СрокДействияПоследнейМеткиВремени = СертификатПоследнейМеткиВремени.ДатаОкончания;
		ДатаПодписиИзМетки = Мин(ДатаПодписиИзМетки, Подпись.АрхивныеМеткиВремени[0].Дата + ДобавкаВремени);
		ПараметрыПодписи.ДатаПоследнейМеткиВремени = Подпись.АрхивныеМеткиВремени[ИндексПоследнейМетки].Дата;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(СрокДействияПоследнейМеткиВремени) Тогда
		ПараметрыПодписи.СрокДействияПоследнейМеткиВремени = СрокДействияПоследнейМеткиВремени + ДобавкаВремени; 
		ПараметрыПодписи.СертификатПоследнейМеткиВремени = СертификатПоследнейМеткиВремени;
	ИначеЕсли СертификатСуществует И ПараметрыПодписи.ОписаниеСертификата.ДействителенДо < ДатаСеанса Тогда
		ПараметрыПодписи.СрокДействияПоследнейМеткиВремени = ПараметрыПодписи.ОписаниеСертификата.ДействителенДо;
		ПараметрыПодписи.СертификатПоследнейМеткиВремени = Подпись.СертификатПодписи;
	ИначеЕсли СертификатСуществует Тогда
		ПараметрыПодписи.СертификатПоследнейМеткиВремени = Подпись.СертификатПодписи;
	КонецЕсли;

	Если ДатаПодписиИзМетки <> Дата(3999, 12, 31) Тогда
		ПараметрыПодписи.ДатаПодписиИзМетки = ДатаПодписиИзМетки;
	КонецЕсли;
		
	Возврат ПараметрыПодписи;
	
КонецФункции

// Только для внутреннего использования.
Функция ТипПодписиКриптографии(ТипПодписиЗначение) Экспорт
	
	#Если МобильныйКлиент Тогда
	
	Возврат Неопределено;
	
	#Иначе

	Если ТипЗнч(ТипПодписиЗначение) = Тип("ТипПодписиКриптографии") Тогда
		Если ТипПодписиЗначение = ТипПодписиКриптографии.CAdESBES Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdEST Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESAv3 Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.АрхивнаяCAdESAv3");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESC Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СПолнымНаборомПроверочныхДанныхCAdESC");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESXLongType2 Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.РасширеннаяДолгосрочнаяCAdESXLongType2");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESAv2 Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESAv2");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CMS Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.ОбычнаяCMS");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESXLong Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXLong");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESXLongType1 Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXLongType1");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESXType1  Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXType1");
		ИначеЕсли ТипПодписиЗначение = ТипПодписиКриптографии.CAdESXType2 Тогда
			Возврат ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXType2");
		КонецЕсли;
	Иначе
		Если ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES") Тогда
			Возврат ТипПодписиКриптографии.CAdESBES;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST") Тогда
			Возврат ТипПодписиКриптографии.CAdEST;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.АрхивнаяCAdESAv3") Тогда
			Возврат ТипПодписиКриптографии.CAdESAv3;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СПолнымНаборомПроверочныхДанныхCAdESC") Тогда
			Возврат ТипПодписиКриптографии.CAdESC;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.РасширеннаяДолгосрочнаяCAdESXLongType2") Тогда
			Возврат ТипПодписиКриптографии.CAdESXLongType2;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESAv2") Тогда
			Возврат ТипПодписиКриптографии.CAdESAv2;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.ОбычнаяCMS") Тогда
			Возврат ТипПодписиКриптографии.CMS;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXLong") Тогда
			Возврат ТипПодписиКриптографии.CAdESXLong;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXLongType1") Тогда
			Возврат ТипПодписиКриптографии.CAdESXLongType1;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXType1") Тогда
			Возврат ТипПодписиКриптографии.CAdESXType1;
		ИначеЕсли ТипПодписиЗначение = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXType2") Тогда
			Возврат ТипПодписиКриптографии.CAdESXType2;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Неопределено;
	
	#КонецЕсли
	
КонецФункции

// Только для внутреннего использования.
Функция НастройкиСозданияПодписи(ТипПодписи, АдресаСерверовМетокВремени) Экспорт
	
	Результат = Новый Структура("ТипПодписи, АдресаСерверовМетокВремени");
	Результат.АдресаСерверовМетокВремени = АдресаСерверовМетокВремени;
	
	Если Не ЗначениеЗаполнено(ТипПодписи) Тогда
		Результат.ТипПодписи = ТипПодписиКриптографии(
			ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES"));
		Возврат Результат;
	КонецЕсли;
	
	Если ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST") 
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СПолнымНаборомПроверочныхДанныхCAdESC")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.РасширеннаяДолгосрочнаяCAdESXLongType2")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.АрхивнаяCAdESAv3") Тогда 
		
		Если Результат.АдресаСерверовМетокВремени.Количество() = 0 Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru='Для создания подписи типа ""%1"" заполните адреса серверов штампов времени.'"), ТипПодписи);
		КонецЕсли;
		
	ИначеЕсли ТипПодписи <> ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES")
		И ТипПодписи <> ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.ОбычнаяCMS") Тогда
		
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru='Создание подписи типа ""%1"" не поддерживается.'"), ТипПодписи);
	КонецЕсли;
	
	Результат.ТипПодписи = ТипПодписиКриптографии(ТипПодписи);
	
	Возврат Результат;
	
КонецФункции

// Только для внутреннего использования.
Функция ПодлежитУсовершенствованию(ТипПодписи, НовыйТипПодписи) Экспорт
	
	Если НовыйТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST")
		И ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES") Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если НовыйТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.АрхивнаяCAdESAv3")
		И (ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СПолнымНаборомПроверочныхДанныхCAdESC")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXLong")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXType1")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXType2")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.CAdESXLongType1")
		Или ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.РасширеннаяДолгосрочнаяCAdESXLongType2")) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Только для внутреннего использования.
Функция ЗаголовокОшибкиПолученияДанных(Операция) Экспорт
	
	Если Операция = "Подписание" Тогда
		Возврат НСтр("ru = 'Не удалось получить данные для подписания по причине:'");
	ИначеЕсли Операция = "Шифрование" Тогда
		Возврат НСтр("ru = 'Не удалось получить данные для шифрования по причине:'");
	ИначеЕсли Операция = "ПродлениеСрокаДействияПодписи" Тогда
		Возврат НСтр("ru = 'Не удалось получить данные подписи для продления по причине:'");
	Иначе
		Возврат НСтр("ru = 'Не удалось получить данные для расшифровки по причине:'");
	КонецЕсли;
	
КонецФункции

// Только для внутреннего использования.
Функция ПустыеДанныеПодписи(ДанныеПодписи, ОписаниеОшибки) Экспорт
	
	Если Не ЗначениеЗаполнено(ДанныеПодписи) Тогда
		ОписаниеОшибки = НСтр("ru = 'Сформирована пустая подпись.'");
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Только для внутреннего использования.
Функция ПустыеЗашифрованныеДанные(ЗашифрованныеДанные, ОписаниеОшибки) Экспорт
	
	Если Не ЗначениеЗаполнено(ЗашифрованныеДанные) Тогда
		ОписаниеОшибки = НСтр("ru = 'Сформированы пустые зашифрованные данные.'");
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Только для внутреннего использования.
Функция ПустыеРасшифрованныеДанные(РасшифрованныеДанные, ОписаниеОшибки) Экспорт
	
	Если Не ЗначениеЗаполнено(РасшифрованныеДанные) Тогда
		ОписаниеОшибки = НСтр("ru = 'Сформированы пустые расшифрованные данные.'");
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Только для внутреннего использования.
Функция ОбщееОписаниеОшибки(ОшибкаНаКлиенте, ОшибкаНаСервере, ЗаголовокОшибки = "") Экспорт
	
	ОписаниеОшибкиНаКлиенте = УпрощеннаяСтруктураОшибки(ОшибкаНаКлиенте, ЗаголовокОшибки);
	ОписаниеОшибкиНаСервере = УпрощеннаяСтруктураОшибки(ОшибкаНаСервере, ЗаголовокОшибки);
	
	Если Не ЗначениеЗаполнено(ОписаниеОшибкиНаКлиенте.ОписаниеОшибки)
	   И Не ЗначениеЗаполнено(ОписаниеОшибкиНаСервере.ОписаниеОшибки) Тогда
	
		ОбщееОписаниеОшибки = НСтр("ru = 'Непредвиденная ситуация'");
		
	ИначеЕсли Не ЗначениеЗаполнено(ОписаниеОшибкиНаКлиенте.ОписаниеОшибки)
	      Или ОписаниеОшибкиНаКлиенте.НеПоддерживается
	        И ЗначениеЗаполнено(ОписаниеОшибкиНаСервере.ОписаниеОшибки) Тогда
		
		Если ЗначениеЗаполнено(ОписаниеОшибкиНаСервере.ЗаголовокОшибки)
		   И ЗначениеЗаполнено(ОписаниеОшибкиНаСервере.Описание) Тогда
		
			ОбщееОписаниеОшибки =
				  ОписаниеОшибкиНаСервере.ЗаголовокОшибки
				+ Символы.ПС + Символы.ПС
				+ НСтр("ru = 'НА СЕРВЕРЕ:'")
				+ Символы.ПС + Символы.ПС + ОписаниеОшибкиНаСервере.Описание;
		Иначе
			ОбщееОписаниеОшибки =
				  НСтр("ru = 'НА СЕРВЕРЕ:'")
				+ Символы.ПС + Символы.ПС + ОписаниеОшибкиНаСервере.ОписаниеОшибки;
		КонецЕсли;
		
	ИначеЕсли Не ЗначениеЗаполнено(ОписаниеОшибкиНаСервере.ОписаниеОшибки) Тогда
		ОбщееОписаниеОшибки = ОписаниеОшибкиНаКлиенте.ОписаниеОшибки;
	Иначе
		Если ОписаниеОшибкиНаКлиенте.ЗаголовокОшибки = ОписаниеОшибкиНаСервере.ЗаголовокОшибки
		   И ЗначениеЗаполнено(ОписаниеОшибкиНаКлиенте.ЗаголовокОшибки) Тогда
			
			ОбщееОписаниеОшибки = ОписаниеОшибкиНаКлиенте.ЗаголовокОшибки + Символы.ПС + Символы.ПС;
			ТекстОшибкиНаКлиенте = ОписаниеОшибкиНаКлиенте.Описание;
			ТекстОшибкиНаСервере = ОписаниеОшибкиНаСервере.Описание;
		Иначе
			ОбщееОписаниеОшибки = "";
			ТекстОшибкиНаКлиенте = ОписаниеОшибкиНаКлиенте.ОписаниеОшибки;
			ТекстОшибкиНаСервере = ОписаниеОшибкиНаСервере.ОписаниеОшибки;
		КонецЕсли;
		
		ОбщееОписаниеОшибки = ОбщееОписаниеОшибки
			+ НСтр("ru = 'НА СЕРВЕРЕ:'")
			+ Символы.ПС + Символы.ПС + ТекстОшибкиНаСервере
			+ Символы.ПС + Символы.ПС
			+ НСтр("ru = 'НА КОМПЬЮТЕРЕ:'")
			+ Символы.ПС + Символы.ПС + ТекстОшибкиНаКлиенте;
	КонецЕсли;
	
	Возврат ОбщееОписаниеОшибки;
	
КонецФункции

// Только для внутреннего использования.
Функция ДатаПодписанияУниверсальная(Данные) Экспорт
	
	ДвоичныеДанные = ДвоичныеДанныеИзДанных(Данные,
		"ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмПодписи");
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
		
	// SEQUENCE (PKCS #7 ContentInfo).
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
		// OBJECT IDENTIFIER (contentType).
		ПропуститьНачалоБлока(АнализДанных, 0, 6);
			// 1.2.840.113549.1.7.2 signedData (PKCS #7).
			ПроверитьДанныеБлока(АнализДанных, "2A864886F70D010702");
			ПропуститьРодительскийБлок(АнализДанных);
		// [0]CS             (content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL).
		ПропуститьНачалоБлока(АнализДанных, 2, 0);
			// SEQUENCE (content SignedData).
			ПропуститьНачалоБлока(АнализДанных, 0, 16);
				// INTEGER  (version          Version).
				ПропуститьБлок(АнализДанных, 0, 2);
				// SET      (digestAlgorithms DigestAlgorithmIdentifiers).
				ПропуститьБлок(АнализДанных, 0, 17);
				// SEQUENCE (contentInfo      ContentInfo).
				ПропуститьБлок(АнализДанных, 0, 16);
				// [0]CS    (certificates     [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL).
				ПропуститьБлок(АнализДанных, 2, 0, Ложь);
				// [1]CS    (crls             [1] IMPLICIT CertificateRevocationLists OPTIONAL).
				ПропуститьБлок(АнализДанных, 2, 1, Ложь);
				// SET      (signerInfos      SET OF SignerInfo).
				ПропуститьНачалоБлока(АнализДанных, 0, 17);
					// SEQUENCE (signerInfo SignerInfo).
					ПропуститьНачалоБлока(АнализДанных, 0, 16);
						// INTEGER  (version                   Version).
						ПропуститьБлок(АнализДанных, 0, 2);
						// SEQUENCE (issuerAndSerialNumber     IssuerAndSerialNumber).
						ПропуститьБлок(АнализДанных, 0, 16);
						// SEQUENCE (digestAlgorithm           DigestAlgorithmIdentifier).
						ПропуститьБлок(АнализДанных, 0, 16);
						// [0]CS    (authenticatedAttributes   [0] IMPLICIT Attributes OPTIONAL).
						ПропуститьНачалоБлока(АнализДанных, 2, 0);

	Если АнализДанных.ЕстьОшибка Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	СмещениеСледующего = АнализДанных.Родители[0].СмещениеСледующего;
	Пока АнализДанных.Смещение < СмещениеСледующего Цикл
		
		// SEQUENCE (Attributes).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);
		
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат Неопределено;
		КонецЕсли; 
		
		// OBJECT IDENTIFIER
		ПропуститьНачалоБлока(АнализДанных, 0, 6);
		
		РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
		Если РазмерДанных = 0 Тогда
			ПриОшибкеСтруктурыДанных(АнализДанных);
			Возврат Неопределено;
		КонецЕсли;
		
		Если РазмерДанных = 9 Тогда
			Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
			СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
			Если СтрокаБуфера = "2A864886F70D010905" Тогда // 1.2.840.113549.1.9.5 signingTime
				
				ДатаПодписания = ПрочитатьДатуИзБуфера(АнализДанных.Буфер, АнализДанных.Смещение + 11);
				
				Если ЗначениеЗаполнено(ДатаПодписания) Тогда
					Возврат ДатаПодписания;
				Иначе
					Возврат Неопределено;
				КонецЕсли;
				
			КонецЕсли;
		КонецЕсли; 
		ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER
		ПропуститьРодительскийБлок(АнализДанных); // SEQUENCE
	КонецЦикла;
	
	Возврат Неопределено;
	
КонецФункции

// Только для внутреннего использования.
Функция СвойстваПодписиИзДвоичныхДанных(Данные, ДобавкаВремени = Неопределено, ПрочитатьСертификаты = Ложь) Экспорт
	
	СвойстваПодписи = Новый Структура;
	СвойстваПодписи.Вставить("ТипПодписи", ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.ОбычнаяCMS"));
	СвойстваПодписи.Вставить("ДатаПодписания");
	СвойстваПодписи.Вставить("ДатаШтампаВремени");
	СвойстваПодписи.Вставить("Сертификаты", Новый Массив);
	
	ДвоичныеДанные = ДвоичныеДанныеИзДанных(Данные,
		"ЭлектроннаяПодписьСлужебныйКлиентСервер.СвойстваПодписиИзДвоичныхДанных");
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
		
	// SEQUENCE (PKCS #7 ContentInfo).
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
		// OBJECT IDENTIFIER (contentType).
		ПропуститьНачалоБлока(АнализДанных, 0, 6);
			// 1.2.840.113549.1.7.2 signedData (PKCS #7).
			ПроверитьДанныеБлока(АнализДанных, "2A864886F70D010702");
			Если АнализДанных.ЕстьОшибка Тогда
				СвойстваПодписи.ТипПодписи = Неопределено;
				Возврат СвойстваПодписи;
			КонецЕсли;
			ПропуститьРодительскийБлок(АнализДанных);
		// [0]CS             (content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL).
		ПропуститьНачалоБлока(АнализДанных, 2, 0);
			// SEQUENCE (content SignedData).
			ПропуститьНачалоБлока(АнализДанных, 0, 16);
				// INTEGER  (version          Version).
				ПропуститьБлок(АнализДанных, 0, 2);
				// SET      (digestAlgorithms DigestAlgorithmIdentifiers).
				ПропуститьБлок(АнализДанных, 0, 17);
				// SEQUENCE (contentInfo      ContentInfo).
				ПропуститьБлок(АнализДанных, 0, 16);
				// [0]CS    (certificates [0] IMPLICIT CertificateSet OPTIONAL).
				Если ПрочитатьСертификаты = Ложь Тогда
					ПропуститьБлок(АнализДанных, 2, 0, Ложь);
				Иначе
					Если ПропуститьНачалоБлока(АнализДанных, 2, 0, Ложь) Тогда
						// CertificateSet ::= SET OF Certificate Choices
						Пока Истина Цикл
							// Certificate
							Сертификат = ПрочитанныйБлок(АнализДанных, 0, 16);
							Если Сертификат = Неопределено Тогда
								Прервать;
							КонецЕсли;
							СвойстваПодписи.Сертификаты.Добавить(Сертификат);
						КонецЦикла;
						ПропуститьРодительскийБлок(АнализДанных);
					КонецЕсли;
				КонецЕсли;
				// [1]CS    (crls             [1] IMPLICIT CertificateRevocationLists OPTIONAL).
				ПропуститьБлок(АнализДанных, 2, 1, Ложь);
				// SET      (signerInfos      SET OF SignerInfo).
				ПропуститьНачалоБлока(АнализДанных, 0, 17);
					// SEQUENCE (signerInfo SignerInfo).
					ПропуститьНачалоБлока(АнализДанных, 0, 16);
						// INTEGER  (version                   Version).
						ПропуститьБлок(АнализДанных, 0, 2);
						// SEQUENCE (issuerAndSerialNumber     IssuerAndSerialNumber).
						ПропуститьБлок(АнализДанных, 0, 16);
						// SEQUENCE (digestAlgorithm           DigestAlgorithmIdentifier).
						ПропуститьБлок(АнализДанных, 0, 16);
						// [0]CS    (authenticatedAttributes   [0] IMPLICIT Attributes OPTIONAL).
						ПропуститьНачалоБлока(АнализДанных, 2, 0);

	Если АнализДанных.ЕстьОшибка Тогда
		Возврат СвойстваПодписи;
	КонецЕсли;
	
	ЕстьДайджестСообщения = Ложь; // 1.2.840.113549.1.9.4
	ЕстьТипКонтента = Ложь; // 1.2.840.113549.1.9.3
	ЕстьВеткаСертификата = Ложь; // 1.2.840.113549.1.9.16.2.47 или 1.2.840.113549.1.9.16.2.12

	СмещениеСледующего = АнализДанных.Родители[0].СмещениеСледующего;
	Пока АнализДанных.Смещение < СмещениеСледующего И Не АнализДанных.ЕстьОшибка Цикл
		
		// SEQUENCE (Attributes).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);
		
		Если АнализДанных.ЕстьОшибка Тогда
			Прервать;
		КонецЕсли; 
		
		// OBJECT IDENTIFIER
		ПропуститьНачалоБлока(АнализДанных, 0, 6);
		
		РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
		Если РазмерДанных = 0 Тогда
			ПриОшибкеСтруктурыДанных(АнализДанных);
			Прервать;
		КонецЕсли;
				
		Если РазмерДанных = 9 Тогда
			Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
			СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);

			Если СтрокаБуфера = "2A864886F70D010904" Тогда // 1.2.840.113549.1.9.4 messageDigest
				ЕстьДайджестСообщения = Истина;
			ИначеЕсли СтрокаБуфера = "2A864886F70D010903" Тогда // 1.2.840.113549.1.9.3 contentType
				ЕстьТипКонтента = Истина;
			ИначеЕсли СтрокаБуфера = "2A864886F70D010905" Тогда // 1.2.840.113549.1.9.5 signingTime
				
				ДатаПодписания = ПрочитатьДатуИзБуфера(АнализДанных.Буфер, АнализДанных.Смещение + 11);
				
				Если ЗначениеЗаполнено(ДатаПодписания) Тогда
					СвойстваПодписи.ДатаПодписания = ДатаПодписания + ?(ЗначениеЗаполнено(ДобавкаВремени),
						ДобавкаВремени, 0);
				КонецЕсли;
				
			КонецЕсли;
		
		ИначеЕсли РазмерДанных = 11 Тогда
			Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
			СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
			
			Если СтрокаБуфера = "2A864886F70D010910022F" Тогда // 1.2.840.113549.1.9.16.2.47 signingCertificateV2
				ЕстьВеткаСертификата = Истина;
			ИначеЕсли СтрокаБуфера = "2A864886F70D010910020C" Тогда // 1.2.840.113549.1.9.16.2.12 signingCertificate
				ЕстьВеткаСертификата = Истина;
			КонецЕсли;
		КонецЕсли;
		
		ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER
		ПропуститьРодительскийБлок(АнализДанных); // SEQUENCE
	КонецЦикла;
	
	Если ЕстьВеткаСертификата И ЕстьДайджестСообщения И ЕстьТипКонтента Тогда
		СвойстваПодписи.ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.БазоваяCAdESBES");
	Иначе
		Возврат СвойстваПодписи;
	КонецЕсли;
	
	ПропуститьРодительскийБлок(АнализДанных); // [0]CS
	
	// SEQUENCE (digestEncryptionAlgorithm AlgorithmIdentifier).
	ПропуститьБлок(АнализДанных, 0, 16);
	// signature SignatureValue
	ПропуститьБлок(АнализДанных, 0, 4); 
	// [1]CS    (unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL).
	ПропуститьНачалоБлока(АнализДанных, 2, 1);

	ЕстьВеткаМеткиВремени = Ложь; // 1.2.840.113549.1.9.16.2.14 
	ЕстьВеткаОписаниеСертификатов = Ложь; // 1.2.840.113549.1.9.16.2.21
	ЕстьВеткаОписаниеОтзыва = Ложь; // 1.2.840.113549.1.9.16.2.22
	ЕстьВеткаЗначениеСертификатов = Ложь; // 1.2.840.113549.1.9.16.2.23
	ЕстьВеткаЗначениеОтзыва = Ложь; // 1.2.840.113549.1.9.16.2.24
	ЕстьВеткаСписокСерверовОтзыва = Ложь; // 1.2.840.113549.1.9.16.2.26
	ЕстьВеткаАрхива = Ложь; // 0.4.0.1733.2.5
	
	СмещениеСледующего = АнализДанных.Родители[0].СмещениеСледующего;
	Пока АнализДанных.Смещение < СмещениеСледующего И Не АнализДанных.ЕстьОшибка Цикл
		
		// SEQUENCE (Attributes).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);

		Если АнализДанных.ЕстьОшибка Тогда
			Прервать;
		КонецЕсли; 
		
		// OBJECT IDENTIFIER
		ПропуститьНачалоБлока(АнализДанных, 0, 6);

		РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
		Если РазмерДанных = 0 Тогда
			ПриОшибкеСтруктурыДанных(АнализДанных);
			Прервать;
		КонецЕсли;
		
		Если РазмерДанных = 11 Тогда
			
			Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
			СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
			Если СтрокаБуфера = "2A864886F70D010910020E" Тогда // 1.2.840.113549.1.9.16.2.14 timeStampToken
				
				ЕстьВеткаМеткиВремени = Истина;
				
				РазмерДанных = АнализДанных.Родители[1].РазмерДанных - 13;
				
				// SET
				Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение + 11, РазмерДанных);
				ДатаШтампаВремени = ПрочитатьДатуИзШтампаВремени(Буфер);
				Если ЗначениеЗаполнено(ДатаШтампаВремени) Тогда
					СвойстваПодписи.ДатаШтампаВремени = ДатаШтампаВремени + ?(ЗначениеЗаполнено(ДобавкаВремени),
						ДобавкаВремени, 0);
				КонецЕсли;
				
			ИначеЕсли СтрокаБуфера = "2A864886F70D0109100215" Тогда // 1.2.840.113549.1.9.16.2.21 certificateRefs
				ЕстьВеткаОписаниеСертификатов = Истина;	
			ИначеЕсли СтрокаБуфера = "2A864886F70D0109100216" Тогда // 1.2.840.113549.1.9.16.2.22 revocationRefs
				ЕстьВеткаОписаниеОтзыва = Истина;
			ИначеЕсли СтрокаБуфера = "2A864886F70D0109100217" Тогда // 1.2.840.113549.1.9.16.2.23 certValues
				ЕстьВеткаЗначениеСертификатов = Истина;
			ИначеЕсли СтрокаБуфера = "2A864886F70D0109100218" Тогда // 1.2.840.113549.1.9.16.2.24 revocationValues
				ЕстьВеткаЗначениеОтзыва = Истина;
			ИначеЕсли СтрокаБуфера = "2A864886F70D010910021A" Тогда // 1.2.840.113549.1.9.16.2.26 certCRLTimestamp
				ЕстьВеткаСписокСерверовОтзыва = Истина; 
			КонецЕсли;
			
		КонецЕсли;

		Если РазмерДанных = 6 Тогда
			Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
			СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
			Если СтрокаБуфера = "04008D450204" Тогда // 0.4.0.1733.2.4 archiveTimestampV3 attribute
				ЕстьВеткаАрхива = Истина;
			КонецЕсли;
		КонецЕсли;

		ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER
		ПропуститьРодительскийБлок(АнализДанных); // SEQUENCE
	КонецЦикла;

	ТипПодписи = Неопределено;
	Если ЕстьВеткаАрхива Тогда
		ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.АрхивнаяCAdESAv3");
	ИначеЕсли ЕстьВеткаМеткиВремени И ЕстьВеткаОписаниеСертификатов И ЕстьВеткаОписаниеОтзыва
		И ЕстьВеткаЗначениеСертификатов И ЕстьВеткаЗначениеОтзыва И ЕстьВеткаСписокСерверовОтзыва Тогда
		ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.РасширеннаяДолгосрочнаяCAdESXLongType2");
	ИначеЕсли ЕстьВеткаМеткиВремени И ЕстьВеткаОписаниеСертификатов И ЕстьВеткаОписаниеОтзыва Тогда
		ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СПолнымНаборомПроверочныхДанныхCAdESC");
	ИначеЕсли ЕстьВеткаМеткиВремени Тогда
		ТипПодписи = ПредопределенноеЗначение("Перечисление.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST");
	КонецЕсли;

	Если ЗначениеЗаполнено(ТипПодписи) Тогда
		СвойстваПодписи.ТипПодписи = ТипПодписи;
	КонецЕсли;
	
	Возврат СвойстваПодписи;
	
КонецФункции

Функция ПрочитатьДатуИзБуфера(Буфер, Смещение) Экспорт
	
	ТипДаты = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер.Прочитать(Смещение, 2));
	Если ТипДаты = "170D" Тогда // UTCTime
		БуферДаты = Буфер.Прочитать(Смещение + 2, 12);
		ПредставлениеДаты = "20" + ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДаты);
	Иначе // GeneralizedTime
		БуферДаты = Буфер.Прочитать(Смещение + 2, 14);
		ПредставлениеДаты = ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДаты);
	КонецЕсли;

	ОписаниеТипа = Новый ОписаниеТипов("Дата");
	ДатаПодписания = ОписаниеТипа.ПривестиЗначение(ПредставлениеДаты);
	Возврат ДатаПодписания;
	
КонецФункции

// Для функции СвойстваПодписиИзДвоичныхДанных.
Функция ПрочитатьДатуИзШтампаВремени(Буфер)
	
	АнализДанных = Новый Структура;
	АнализДанных.Вставить("ЕстьОшибка", Ложь);
	АнализДанных.Вставить("ЭтоОшибкаКодированияASN1", Ложь); // Возможно данные повреждены.
	АнализДанных.Вставить("ЭтоОшибкаСтруктурыДанных", Ложь); // Не найден ожидаемый элемент данных.
	АнализДанных.Вставить("Смещение", 0);
	АнализДанных.Вставить("Родители", Новый Массив);
	АнализДанных.Вставить("Буфер", Буфер);
	
	// SET
	ПропуститьНачалоБлока(АнализДанных, 0, 17);
	// SEQUENCE
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
	// OBJECT IDENTIFIER signedData
	ПропуститьБлок(АнализДанных, 0, 6);
		// [0]
		ПропуститьНачалоБлока(АнализДанных, 2, 0);
			// SEQUENCE
			ПропуститьНачалоБлока(АнализДанных, 0, 16);
			// INTEGER  (version          Version).
			ПропуститьБлок(АнализДанных, 0, 2);
			// SET
			ПропуститьБлок(АнализДанных, 0, 17);
				// SEQUENCE
				ПропуститьНачалоБлока(АнализДанных, 0, 16); 
				// OBJECT IDENTIFIER
				ПропуститьБлок(АнализДанных, 0, 6); 
					// [0]
					ПропуститьНачалоБлока(АнализДанных, 2, 0);
						// OCTET STRING
						ПропуститьНачалоБлока(АнализДанных, 0, 4);
						// SEQUENCE
						ПропуститьНачалоБлока(АнализДанных, 0, 16);
						// INTEGER
						ПропуститьБлок(АнализДанных, 0, 2);
						// OBJECT IDENTIFIER
						ПропуститьБлок(АнализДанных, 0, 6);
						// SEQUENCE
						ПропуститьБлок(АнализДанных, 0, 16);
						// INTEGER
						ПропуститьБлок(АнализДанных, 0, 2);

	Если АнализДанных.ЕстьОшибка Тогда
		ДатаШтампа = Неопределено;
	Иначе
		// GeneralizedTime
		БуферДаты = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение + 2, 14);
		ПредставлениеДаты = ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДаты);
		ОписаниеТипа = Новый ОписаниеТипов("Дата");
		ДатаШтампа = ОписаниеТипа.ПривестиЗначение(ПредставлениеДаты);
	КонецЕсли;
	
	Возврат ДатаШтампа;
	
КонецФункции

// Находит в XML содержимое находящееся в теге.
//
// Параметры:
//  Текст                             - Строка - текст XML, в котором выполняется поиск.
//  ИмяТега                           - Строка - тег, содержимое которого необходимо найти.
//  ВключатьОткрывающийЗакрывающийТег - Булево - признак необходимости найденного тегом,
//                                               по которому выполнялся поиск, по умолчанию Ложь.
//  НомерПоПорядку                    - Число  - позиция, с которой начинается поиск, по умолчанию 1.
// 
// Возвращаемое значение:
//   Строка - строка, из которой удалены символы перевода строки и возврата каретки.
//
Функция НайтиВXML(Текст, ИмяТега, ВключатьОткрывающийЗакрывающийТег = Ложь, НомерПоПорядку = 1) Экспорт
	
	Результат = Неопределено;
	
	Начало    = "<"  + ИмяТега;
	Окончание = "</" + ИмяТега + ">";
	
	Содержимое = Сред(
		Текст,
		СтрНайти(Текст, Начало, НаправлениеПоиска.СНачала, 1, НомерПоПорядку),
		СтрНайти(Текст, Окончание, НаправлениеПоиска.СНачала, 1, НомерПоПорядку) + СтрДлина(Окончание) - СтрНайти(Текст, Начало, НаправлениеПоиска.СНачала, 1, НомерПоПорядку));
		
	Если ВключатьОткрывающийЗакрывающийТег Тогда
		
		Результат = СокрЛП(Содержимое);
		
	Иначе
		
		ОткрывающийТег = Лев(Содержимое, СтрНайти(Содержимое, ">"));
		Содержимое = СтрЗаменить(Содержимое, ОткрывающийТег, "");
		
		ЗакрывающийТег = Прав(Содержимое, СтрДлина(Содержимое) - СтрНайти(Содержимое, "<", НаправлениеПоиска.СКонца) + 1);
		Содержимое = СтрЗаменить(Содержимое, ЗакрывающийТег, "");
		
		Результат = СокрЛП(Содержимое);
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Только для внутреннего использования.
Функция СертификатИзКонвертаSOAP(КонвертSOAP, ВФорматеBase64 = Истина) Экспорт
	
	СертификатBase64 = НайтиВXML(КонвертSOAP, "wsse:BinarySecurityToken");
	
	Если ВФорматеBase64 Тогда
		Возврат СертификатBase64;
	КонецЕсли;
	
	Возврат Base64Значение(СертификатBase64);
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПредставлениеСертификата.
Функция ПредставлениеСертификата(Сертификат, ДобавкаВремени = Неопределено) Экспорт
	
	Представление = "";
	ЭлектроннаяПодписьКлиентСерверЛокализация.ПриПолученииПредставленияСертификата(Сертификат, ДобавкаВремени, Представление);
	
	Если ПустаяСтрока(Представление) Тогда
		Если ТипЗнч(Сертификат) = Тип("Структура") Тогда
			Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1, до %2'"),
				ПредставлениеСубъекта(Сертификат.Сертификат),
				Формат(Сертификат.ДействителенДо, "ДФ=MM.yyyy"));
		Иначе
			ДатыСертификата = ДатыСертификата(Сертификат, ДобавкаВремени);
			Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1, до %2'"),
				ПредставлениеСубъекта(Сертификат),
				Формат(ДатыСертификата.ДатаОкончания, "ДФ=MM.yyyy"));
		КонецЕсли;
	КонецЕсли;
	
	Возврат Представление;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПредставлениеСубъекта.
Функция ПредставлениеСубъекта(Сертификат) Экспорт 
	
	Представление = "";
	ЭлектроннаяПодписьКлиентСерверЛокализация.ПриПолученииПредставленияСубъекта(Сертификат, Представление);
	
	Если ПустаяСтрока(Представление) Тогда
		
		Субъект = СвойстваСубъектаСертификата(Сертификат);
		
		Если ЗначениеЗаполнено(Субъект.Фамилия) И ЗначениеЗаполнено(Субъект.Имя) Тогда
			Представление = Субъект.Фамилия + " " + Субъект.Имя;
		ИначеЕсли ЗначениеЗаполнено(Субъект.Фамилия) Тогда
			Представление = Субъект.Фамилия;
		ИначеЕсли ЗначениеЗаполнено(Субъект.Имя) Тогда
			Представление = Субъект.Имя;
		КонецЕсли;

		Если ЗначениеЗаполнено(Представление) Тогда
			Если ЗначениеЗаполнено(Субъект.Организация) Тогда
				Представление = Представление + ", " + Субъект.Организация;
			КонецЕсли;
			Если ЗначениеЗаполнено(Субъект.Подразделение) Тогда
				Представление = Представление + ", " + Субъект.Подразделение;
			КонецЕсли;
		ИначеЕсли ЗначениеЗаполнено(Субъект.ОбщееИмя) Тогда
			Представление = Субъект.ОбщееИмя;
		КонецЕсли;

	КонецЕсли;

	Возврат Представление;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПредставлениеИздателя.
Функция ПредставлениеИздателя(Сертификат) Экспорт
	
	Издатель = СвойстваИздателяСертификата(Сертификат);
	
	Представление = "";
	
	Если ЗначениеЗаполнено(Издатель.ОбщееИмя) Тогда
		Представление = Издатель.ОбщееИмя;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Издатель.ОбщееИмя)
	   И ЗначениеЗаполнено(Издатель.Организация)
	   И СтрНайти(Издатель.ОбщееИмя, Издатель.Организация) = 0 Тогда
		
		Представление = Издатель.ОбщееИмя + ", " + Издатель.Организация;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Издатель.Подразделение) Тогда
		Представление = Представление + ", " + Издатель.Подразделение;
	КонецЕсли;
	
	Возврат Представление;
	
КонецФункции

Функция СвойстваСертификата(Сертификат, ДобавкаВремени, ДвоичныеДанныеСертификата = Неопределено) Экспорт
	
	ДатыСертификата = ДатыСертификата(Сертификат, ДобавкаВремени);
	
	Свойства = Новый Структура;
	Свойства.Вставить("Отпечаток",      Base64Строка(Сертификат.Отпечаток));
	Свойства.Вставить("СерийныйНомер",  Сертификат.СерийныйНомер);
	Свойства.Вставить("КомуВыдан",      ПредставлениеСубъекта(Сертификат));
	Свойства.Вставить("КемВыдан",       ПредставлениеИздателя(Сертификат));
	Свойства.Вставить("ДатаНачала",     ДатыСертификата.ДатаНачала);
	Свойства.Вставить("ДатаОкончания",  ДатыСертификата.ДатаОкончания);
	Свойства.Вставить("Назначение",     ПолучитьНазначение(Сертификат));
	Свойства.Вставить("Подписание",     Сертификат.ИспользоватьДляПодписи);
	Свойства.Вставить("Шифрование",     Сертификат.ИспользоватьДляШифрования);
		
	Если ДвоичныеДанныеСертификата <> Неопределено Тогда
		
		ДополнительныеСвойстваСертификата = ДополнительныеСвойстваСертификата(ДвоичныеДанныеСертификата, ДобавкаВремени);
		
		Если ЗначениеЗаполнено(ДополнительныеСвойстваСертификата.ДатаОкончанияЗакрытогоКлюча) Тогда
			Свойства.Вставить("ДействителенДо",
				Мин(ДатыСертификата.ДатаОкончания, ДополнительныеСвойстваСертификата.ДатаОкончанияЗакрытогоКлюча));
		Иначе
			Свойства.Вставить("ДействителенДо", ДатыСертификата.ДатаОкончания);
		КонецЕсли;
		
		Свойства.Вставить("ДатаНачалаЗакрытогоКлюча",    ДополнительныеСвойстваСертификата.ДатаНачалаЗакрытогоКлюча);
		Свойства.Вставить("ДатаОкончанияЗакрытогоКлюча", ДополнительныеСвойстваСертификата.ДатаОкончанияЗакрытогоКлюча);
		
		Свойства.Вставить("Представление",  ПредставлениеСертификата(
			Новый Структура("Сертификат, ДействителенДо", Сертификат, Свойства.ДействителенДо),
			ДобавкаВремени));
	Иначе
		Свойства.Вставить("ДействителенДо", ДатыСертификата.ДатаОкончания);
		Свойства.Вставить("Представление",  ПредставлениеСертификата(Сертификат, ДобавкаВремени));
	КонецЕсли;
	
	Возврат Свойства;
	
КонецФункции

// Заполняет таблицу описания сертификата из четырех полей: КомуВыдан, КемВыдан, ДействуетДо, Назначение.
Процедура ЗаполнитьОписаниеДанныхСертификата(Таблица, СвойстваСертификата) Экспорт
	
	Если СвойстваСертификата.Подписание И СвойстваСертификата.Шифрование Тогда
		Назначение = НСтр("ru = 'Подписание данных, Шифрование данных'");
		
	ИначеЕсли СвойстваСертификата.Подписание Тогда
		Назначение = НСтр("ru = 'Подписание данных'");
	Иначе
		Назначение = НСтр("ru = 'Шифрование данных'");
	КонецЕсли;
	
	Таблица.Очистить();
	Строка = Таблица.Добавить();
	Строка.Свойство = НСтр("ru = 'Кому выдан:'");
	Строка.Значение = СокрЛП(СвойстваСертификата.КомуВыдан);
	
	Строка = Таблица.Добавить();
	Строка.Свойство = НСтр("ru = 'Кем выдан:'");
	Строка.Значение = СокрЛП(СвойстваСертификата.КемВыдан);
	
	Строка = Таблица.Добавить();
	Строка.Свойство = НСтр("ru = 'Действителен до:'");
	Если ТипЗнч(СвойстваСертификата) <> Тип("Структура") Или СвойстваСертификата.ДействителенДо = СвойстваСертификата.ДатаОкончания Тогда
		Строка.Значение = Формат(СвойстваСертификата.ДействителенДо, "ДЛФ=D");
	Иначе
		Строка.Значение = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1 (срок действия закрытого ключа)'"), Формат(СвойстваСертификата.ДействителенДо, "ДЛФ=D"));
	КонецЕсли;
	
	Строка = Таблица.Добавить();
	Строка.Свойство = НСтр("ru = 'Назначение:'");
	Строка.Значение = Назначение;
	
КонецПроцедуры

Функция СвойстваСубъектаСертификата(Сертификат) Экспорт
	
	Субъект = Сертификат.Субъект;
	
	Свойства = Новый Структура;
	Свойства.Вставить("ОбщееИмя");
	Свойства.Вставить("Страна");
	Свойства.Вставить("Регион");
	Свойства.Вставить("НаселенныйПункт");
	Свойства.Вставить("Улица");
	Свойства.Вставить("Организация");
	Свойства.Вставить("Подразделение");
	Свойства.Вставить("ЭлектроннаяПочта");
	Свойства.Вставить("Фамилия");
	Свойства.Вставить("Имя");
	
	Если Субъект.Свойство("CN") Тогда
		Свойства.ОбщееИмя = ПодготовитьСтроку(Субъект.CN);
	КонецЕсли;
	
	Если Субъект.Свойство("C") Тогда
		Свойства.Страна = ПодготовитьСтроку(Субъект.C);
	КонецЕсли;
	
	Если Субъект.Свойство("ST") Тогда
		Свойства.Регион = ПодготовитьСтроку(Субъект.ST);
	КонецЕсли;
	
	Если Субъект.Свойство("L") Тогда
		Свойства.НаселенныйПункт = ПодготовитьСтроку(Субъект.L);
	КонецЕсли;
	
	Если Субъект.Свойство("Street") Тогда
		Свойства.Улица = ПодготовитьСтроку(Субъект.Street);
	КонецЕсли;
	
	Если Субъект.Свойство("O") Тогда
		Свойства.Организация = ПодготовитьСтроку(Субъект.O);
	КонецЕсли;
	
	Если Субъект.Свойство("OU") Тогда
		Свойства.Подразделение = ПодготовитьСтроку(Субъект.OU);
	КонецЕсли;
	
	Если Субъект.Свойство("E") Тогда
		Свойства.ЭлектроннаяПочта = ПодготовитьСтроку(Субъект.E);
	КонецЕсли;
	
	РасширенныеСвойства = Неопределено;
	ЭлектроннаяПодписьКлиентСерверЛокализация.ПриПолученииРасширенныхСвойствСубъектаСертификата(Субъект, РасширенныеСвойства);
	Если ТипЗнч(РасширенныеСвойства) = Тип("Структура") Тогда
		ОбщегоНазначенияКлиентСервер.ДополнитьСтруктуру(Свойства, РасширенныеСвойства, Истина);
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(Свойства.Фамилия) И Субъект.Свойство("SN") Тогда
		Свойства.Фамилия = ПодготовитьСтроку(Субъект.SN);
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(Свойства.Имя) И Субъект.Свойство("GN") Тогда
		Свойства.Имя = ПодготовитьСтроку(Субъект.GN);
	КонецЕсли;
	
	Возврат Свойства;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.СвойстваИздателяСертификата.
Функция СвойстваИздателяСертификата(Сертификат) Экспорт
	
	Издатель = Сертификат.Издатель;
	
	Свойства = Новый Структура;
	Свойства.Вставить("ОбщееИмя");
	Свойства.Вставить("Страна");
	Свойства.Вставить("Регион");
	Свойства.Вставить("НаселенныйПункт");
	Свойства.Вставить("Улица");
	Свойства.Вставить("Организация");
	Свойства.Вставить("Подразделение");
	Свойства.Вставить("ЭлектроннаяПочта");
	
	Если Издатель.Свойство("CN") Тогда
		Свойства.ОбщееИмя = ПодготовитьСтроку(Издатель.CN);
	КонецЕсли;
	
	Если Издатель.Свойство("C") Тогда
		Свойства.Страна = ПодготовитьСтроку(Издатель.C);
	КонецЕсли;
	
	Если Издатель.Свойство("ST") Тогда
		Свойства.Регион = ПодготовитьСтроку(Издатель.ST);
	КонецЕсли;
	
	Если Издатель.Свойство("L") Тогда
		Свойства.НаселенныйПункт = ПодготовитьСтроку(Издатель.L);
	КонецЕсли;
	
	Если Издатель.Свойство("Street") Тогда
		Свойства.Улица = ПодготовитьСтроку(Издатель.Street);
	КонецЕсли;
	
	Если Издатель.Свойство("O") Тогда
		Свойства.Организация = ПодготовитьСтроку(Издатель.O);
	КонецЕсли;
	
	Если Издатель.Свойство("OU") Тогда
		Свойства.Подразделение = ПодготовитьСтроку(Издатель.OU);
	КонецЕсли;
	
	Если Издатель.Свойство("E") Тогда
		Свойства.ЭлектроннаяПочта = ПодготовитьСтроку(Издатель.E);
	КонецЕсли;
	
	РасширенныеСвойства = Неопределено;
	ЭлектроннаяПодписьКлиентСерверЛокализация.ПриПолученииРасширенныхСвойствИздателяСертификата(Издатель, РасширенныеСвойства);
	Если ТипЗнч(РасширенныеСвойства) = Тип("Структура") Тогда
		ОбщегоНазначенияКлиентСервер.ДополнитьСтруктуру(Свойства, РасширенныеСвойства, Истина);
	КонецЕсли;
	
	Возврат Свойства;
	
КонецФункции

// Удаляет из поля сертификата КомуВыдан все кроме ФИО
// 
// Параметры:
//   КомуВыдан - Массив из Строка
//             - Строка
//            
// Возвращаемое значение:
//   - Массив из Строка - если КомуВыдан массив 
//   - Строка - если КомуВыдан строка
//
Функция ПреобразоватьКомуВыданКВидуФИО(КомуВыдан) Экспорт
	
	Если ТипЗнч(КомуВыдан) = Тип("Массив") Тогда
		Для Каждого ЭлементКомуВыдан Из КомуВыдан Цикл
			ДлинаСтроки = СтрНайти(ЭлементКомуВыдан, ",");
			ЭлементКомуВыдан = СокрЛП(?(ДлинаСтроки = 0, ЭлементКомуВыдан, Лев(ЭлементКомуВыдан, ДлинаСтроки - 1)));
		КонецЦикла; 
	Иначе	
		ДлинаСтроки = СтрНайти(КомуВыдан, ",");
		КомуВыдан = СокрЛП(?(ДлинаСтроки = 0, КомуВыдан, Лев(КомуВыдан, ДлинаСтроки - 1)));
	КонецЕсли;
	
	Возврат КомуВыдан;
	
КонецФункции


Функция ИдентификаторыАлгоритмовХешированияИОткрытогоКлюча() Экспорт
	
	Идентификаторы = Новый Массив;
	
	Наборы = НаборыАлгоритмовДляСозданияПодписи();
	Для Каждого Набор Из Наборы Цикл
		Идентификаторы.Добавить("<" + Набор.ИдентификаторАлгоритмаОткрытогоКлюча + "> <" + Набор.ИдентификаторАлгоритмаХеширования + ">");
	КонецЦикла;
	
	Возврат СтрСоединить(Идентификаторы, Символы.ПС) + Символы.ПС;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.КонвертXML.
Функция КонвертXML(Параметры) Экспорт
	
	Если Параметры = Неопределено Тогда
		Параметры = ПараметрыКонвертаXML();
	КонецЕсли;
	
	КонвертXML = Неопределено;
	ЭлектроннаяПодписьКлиентСерверЛокализация.ПриПолученииКонвертаXML(Параметры, КонвертXML);
	
	Если КонвертXML = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Указано неизвестное значение ""%1"" параметра %2 в функции %3'"),
				Параметры.Вариант, "Вариант", "КонвертXML");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Параметры.СообщениеXML) Тогда
		КонвертXML = СтрЗаменить(КонвертXML, "%MessageXML%", СокрЛП(Параметры.СообщениеXML));
	КонецЕсли;
	
	Возврат КонвертXML;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПараметрыКонвертаXML.
Функция ПараметрыКонвертаXML() Экспорт
	
	Результат = Новый Структура;
	
	ВариантКонверта = "";
	ЭлектроннаяПодписьКлиентСерверЛокализация.ПриПолученииВариантаКонвертаПоУмолчанию(ВариантКонверта);
	
	Результат.Вставить("Вариант", ВариантКонверта);
	Результат.Вставить("СообщениеXML", "");
	
	Возврат Результат;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПараметрыXMLDSig.
Функция ПараметрыXMLDSig() Экспорт
	
	ДанныеАлгоритмаПодписания = Новый Структура;
	
	ДанныеАлгоритмаПодписания.Вставить("XPathSignedInfo",       "");
	ДанныеАлгоритмаПодписания.Вставить("XPathПодписываемыйТег", "");
	
	ДанныеАлгоритмаПодписания.Вставить("OIDАлгоритмаОткрытогоКлюча", "");
	
	ДанныеАлгоритмаПодписания.Вставить("ИмяАлгоритмаПодписи", "");
	ДанныеАлгоритмаПодписания.Вставить("OIDАлгоритмаПодписи", "");
	
	ДанныеАлгоритмаПодписания.Вставить("ИмяАлгоритмаХеширования", "");
	ДанныеАлгоритмаПодписания.Вставить("OIDАлгоритмаХеширования", "");
	
	ДанныеАлгоритмаПодписания.Вставить("АлгоритмПодписи",     "");
	ДанныеАлгоритмаПодписания.Вставить("АлгоритмХеширования", "");
	
	Возврат ДанныеАлгоритмаПодписания;
	
КонецФункции

Функция ТекстОшибкиПроверкиПодписиXML(ПодписьВерна, ХешСовпадает) Экспорт
	
	Если ПодписьВерна Тогда
		ТекстОшибки = НСтр("ru = 'Подпись неверна (%1 корректно, %2 некорректно).'");
	ИначеЕсли ХешСовпадает Тогда
		ТекстОшибки = НСтр("ru = 'Подпись неверна (%1 некорректно, %2 корректно).'");
	Иначе
		ТекстОшибки = НСтр("ru = 'Подпись неверна (%1 некорректно, %2 некорректно).'");
	КонецЕсли;
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки, "SignatureValue", "DigestValue");
	
КонецФункции

// Возвращаемое значение:
//   см. НовыйНаборАлгоритмовДляСозданияПодписи
//  Неопределено - если набор не найден.
//
Функция НаборАлгоритмовДляСозданияПодписи(ИдентификаторАлгоритмаОткрытогоКлюча)
	
	Наборы = НаборыАлгоритмовДляСозданияПодписи();
	Для Каждого Набор Из Наборы Цикл
		Если Набор.ИдентификаторАлгоритмаОткрытогоКлюча = ИдентификаторАлгоритмаОткрытогоКлюча Тогда
			Возврат Набор;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Неопределено;
	
КонецФункции

// Преобразует двоичные данные сертификата криптографии
// в корректно отформатированную строку в формате Base64.
//
// Параметры:
//  ДанныеСертификата - ДвоичныеДанные - двоичные данные сертификата криптографии.
// 
// Возвращаемое значение:
//  Строка - двоичные данные сертификата в формате Base64.
//
Функция СертификатКриптографииBase64(ДанныеСертификата) Экспорт
	
	СтрокаBase64 = Base64Строка(ДанныеСертификата);
	
	Значение = СтрЗаменить(СтрокаBase64, Символы.ВК, "");
	Значение = СтрЗаменить(Значение, Символы.ПС, "");
	
	Возврат Значение;
	
КонецФункции

// Параметры:
//  СертификатКриптографииBase64 - Строка - Base64 строка.
//  ДанныеАлгоритмаПодписания    - см. ЭлектроннаяПодписьКлиент.ПараметрыXMLDSig
//  ВызыватьИсключение           - Булево
//  СвойстваКонвертаXML          - см. ЭлектроннаяПодписьСлужебный.СвойстваКонвертаXML
//  
// Возвращаемое значение:
//   Строка - текст ошибки, если заполнена.
//
Функция ПроверитьВыбратьАлгоритмПодписи(СертификатКриптографииBase64, ДанныеАлгоритмаПодписания,
			ВызыватьИсключение = Ложь, СвойстваКонвертаXML = Неопределено) Экспорт
	
	OIDАлгоритмаОткрытогоКлюча = АлгоритмПодписиСертификата(
		Base64Значение(СертификатКриптографииBase64),, Истина);
	
	Если Не ЗначениеЗаполнено(OIDАлгоритмаОткрытогоКлюча) Тогда
		ТекстОшибки = НСтр("ru = 'Не удалось получить алгоритм открытого ключа из сертификата.'");
		Если ВызыватьИсключение Тогда
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Возврат ТекстОшибки;
	КонецЕсли;
	
	ДанныеАлгоритмаПодписания.Вставить("OIDВыбранногоАлгоритмаПодписи",     Неопределено);
	ДанныеАлгоритмаПодписания.Вставить("OIDВыбранногоАлгоритмаХеширования", Неопределено);
	ДанныеАлгоритмаПодписания.Вставить("ВыбранныйАлгоритмПодписи",          Неопределено);
	ДанныеАлгоритмаПодписания.Вставить("ВыбранныйАлгоритмХеширования",      Неопределено);
	
	OIDАлгоритмовОткрытогоКлюча = СтрРазделить(ДанныеАлгоритмаПодписания.OIDАлгоритмаОткрытогоКлюча, Символы.ПС);
	OIDАлгоритмовПодписи        = СтрРазделить(ДанныеАлгоритмаПодписания.OIDАлгоритмаПодписи,        Символы.ПС);
	OIDАлгоритмовХеширования    = СтрРазделить(ДанныеАлгоритмаПодписания.OIDАлгоритмаХеширования,    Символы.ПС);
	АлгоритмыПодписи            = СтрРазделить(ДанныеАлгоритмаПодписания.АлгоритмПодписи,            Символы.ПС);
	АлгоритмыХеширования        = СтрРазделить(ДанныеАлгоритмаПодписания.АлгоритмХеширования,        Символы.ПС);
	
	АлгоритмыУказаны = Ложь;
	Для Индекс = 0 По OIDАлгоритмовОткрытогоКлюча.Количество() - 1 Цикл
		
		Если OIDАлгоритмаОткрытогоКлюча = OIDАлгоритмовОткрытогоКлюча[Индекс] Тогда
			
			ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаПодписи     = OIDАлгоритмовПодписи[Индекс];
			ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаХеширования = OIDАлгоритмовХеширования[Индекс];
			ДанныеАлгоритмаПодписания.ВыбранныйАлгоритмПодписи          = АлгоритмыПодписи[Индекс];
			ДанныеАлгоритмаПодписания.ВыбранныйАлгоритмХеширования      = АлгоритмыХеширования[Индекс];
			
			АлгоритмыУказаны = Истина;
			Прервать;
			
		КонецЕсли;
		
	КонецЦикла;
	
	Если Не АлгоритмыУказаны Тогда
		НаборАлгоритмов = НаборАлгоритмовДляСозданияПодписи(
			OIDАлгоритмаОткрытогоКлюча);
		
		Если НаборАлгоритмов <> Неопределено Тогда
			ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаПодписи     = НаборАлгоритмов.ИдентификаторАлгоритмаПодписи;
			ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаХеширования = НаборАлгоритмов.ИдентификаторАлгоритмаХеширования;
			ДанныеАлгоритмаПодписания.ВыбранныйАлгоритмПодписи          = НаборАлгоритмов.ИмяАлгоритмаПодписиXML;
			ДанныеАлгоритмаПодписания.ВыбранныйАлгоритмХеширования      = НаборАлгоритмов.ИмяАлгоритмаХешированияXML;
		КонецЕсли;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаПодписи)
	 Или Не ЗначениеЗаполнено(ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаХеширования)
	 Или Не ЗначениеЗаполнено(ДанныеАлгоритмаПодписания.ВыбранныйАлгоритмПодписи)
	 Или Не ЗначениеЗаполнено(ДанныеАлгоритмаПодписания.ВыбранныйАлгоритмХеширования) Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не указаны алгоритмы подписания и хеширования для создания подписи,
			           |соответствующие алгоритму открытого ключа сертификата (OID %1).'"),
			OIDАлгоритмаОткрытогоКлюча);
		
		Если ВызыватьИсключение Тогда
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Возврат ТекстОшибки;
	КонецЕсли;
	
	Если АлгоритмыУказаны
	 Или СвойстваКонвертаXML = Неопределено
	 Или Не СвойстваКонвертаXML.ПроверкаПодписи Тогда
		Возврат "";
	КонецЕсли;
	
	Если СвойстваКонвертаXML.АлгоритмПодписи.Идентификатор
	     <> ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаПодписи Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В документе XML указанный алгоритм подписи
			           |""%1"" (OID ""%2"")
			           |не совпадает с алгоритмом подписи в сертификате OID ""%3"".'"),
			СвойстваКонвертаXML.АлгоритмПодписи.Имя,
			СвойстваКонвертаXML.АлгоритмПодписи.Идентификатор,
			ДанныеАлгоритмаПодписания.OIDВыбранногоАлгоритмаПодписи);
		
		Если ВызыватьИсключение Тогда
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Возврат ТекстОшибки;
	КонецЕсли;
	
	Возврат "";
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПараметрыCMS.
Функция ПараметрыCMS() Экспорт
	
	Параметры = Новый Структура;
	
	Параметры.Вставить("ТипПодписи",   "CAdES-BES");
	Параметры.Вставить("Открепленная", Ложь);
	Параметры.Вставить("ВключениеСертификатовВПодпись",
		РежимВключенияСертификатовКриптографии.ВключатьПолнуюЦепочку);
	
	Возврат Параметры;
	
КонецФункции

Функция ПараметрыКомпонентыCMSSign(ПараметрыCMS, ОписаниеДанных) Экспорт
	
	ПараметрыКомпоненты = Новый Структура;
	
	Если ТипЗнч(ОписаниеДанных) = Тип("Строка")
	   И ЭтоАдресВременногоХранилища(ОписаниеДанных) Тогда
	
		Данные = ПолучитьИзВременногоХранилища(ОписаниеДанных);
	Иначе
		Данные = ОписаниеДанных;
	КонецЕсли;
	
	Если ПараметрыCMS.ТипПодписи = "CAdES-BES" Тогда
		ПараметрыКомпоненты.Вставить("ТипПодписи", 0);
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректный параметр %1 метода %2 компоненты %3.'"),
			"ТипПодписи", "CMSSign", "ExtraCryptoAPI");
	КонецЕсли;
	
	Если ТипЗнч(Данные) = Тип("Строка")
	 Или ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
		
		ПараметрыКомпоненты.Вставить("Данные", Данные);
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректный параметр %1 метода %2 компоненты %3.'"),
			"Данные", "CMSSign", "ExtraCryptoAPI");
	КонецЕсли;
	
	Если ТипЗнч(ПараметрыCMS.Открепленная) = Тип("Булево") Тогда
		ПараметрыКомпоненты.Вставить("Открепленная", ПараметрыCMS.Открепленная);
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректный параметр %1 метода %2 компоненты %3.'"),
			"Открепленная", "CMSSign", "ExtraCryptoAPI");
	КонецЕсли;
	
	//  0 - РежимВключенияСертификатовКриптографии.НеВключать.
	//  1 - РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта.
	// 17 - РежимВключенияСертификатовКриптографии.ВключатьПолнуюЦепочку.
	ПараметрыКомпоненты.Вставить("ВключениеСертификатовВПодпись", 17);
	Если ПараметрыCMS.ВключениеСертификатовВПодпись = "НеВключать"
		Или ПараметрыCMS.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.НеВключать Тогда
		
		ПараметрыКомпоненты.ВключениеСертификатовВПодпись = 0;
	ИначеЕсли ПараметрыCMS.ВключениеСертификатовВПодпись = "ВключатьСертификатСубъекта"
		Или ПараметрыCMS.ВключениеСертификатовВПодпись = РежимВключенияСертификатовКриптографии.ВключатьСертификатСубъекта Тогда
		
		ПараметрыКомпоненты.ВключениеСертификатовВПодпись = 1;
	КонецЕсли;
	
	Возврат ПараметрыКомпоненты;
	
КонецФункции

// Готовит строку для использования в качестве имени файла.
Функция ПодготовитьСтрокуДляИмениФайла(Строка, ЗаменаПробела = Неопределено) Экспорт
	
	ЗаменаСимволов = Новый Соответствие;
	ЗаменаСимволов.Вставить("\", " ");
	ЗаменаСимволов.Вставить("/", " ");
	ЗаменаСимволов.Вставить("*", " ");
	ЗаменаСимволов.Вставить("<", " ");
	ЗаменаСимволов.Вставить(">", " ");
	ЗаменаСимволов.Вставить("|", " ");
	ЗаменаСимволов.Вставить(":", "");
	ЗаменаСимволов.Вставить("""", "");
	ЗаменаСимволов.Вставить("?", "");
	ЗаменаСимволов.Вставить(Символы.ВК, "");
	ЗаменаСимволов.Вставить(Символы.ПС, " ");
	ЗаменаСимволов.Вставить(Символы.Таб, " ");
	ЗаменаСимволов.Вставить(Символы.НПП, " ");
	// замена символов кавычек
	ЗаменаСимволов.Вставить(Символ(171), "");
	ЗаменаСимволов.Вставить(Символ(187), "");
	ЗаменаСимволов.Вставить(Символ(8195), "");
	ЗаменаСимволов.Вставить(Символ(8194), "");
	ЗаменаСимволов.Вставить(Символ(8216), "");
	ЗаменаСимволов.Вставить(Символ(8218), "");
	ЗаменаСимволов.Вставить(Символ(8217), "");
	ЗаменаСимволов.Вставить(Символ(8220), "");
	ЗаменаСимволов.Вставить(Символ(8222), "");
	ЗаменаСимволов.Вставить(Символ(8221), "");
	
	СтрокаПодготовленная = "";
	
	КоличествоСимволов = СтрДлина(Строка);
	
	Для НомерСимвола = 1 По КоличествоСимволов Цикл
		Символ = Сред(Строка, НомерСимвола, 1);
		Если ЗаменаСимволов[Символ] <> Неопределено Тогда
			Символ = ЗаменаСимволов[Символ];
		КонецЕсли;
		СтрокаПодготовленная = СтрокаПодготовленная + Символ;
	КонецЦикла;
	
	Если ЗаменаПробела <> Неопределено Тогда
		СтрокаПодготовленная = СтрЗаменить(ЗаменаПробела, " ", ЗаменаПробела);
	КонецЕсли;
	
	Возврат СокрЛП(СтрокаПодготовленная);
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Вспомогательные процедуры и функции.

// Для функций СертификатПросрочен, ПредставлениеСертификата, СвойстваСертификата.
//
// Параметры:
//   Сертификат - СертификатКриптографии
//
Функция ДатыСертификата(Сертификат, ДобавкаВремени) Экспорт
	
	ДатыСертификата = Новый Структура;
	ДатыСертификата.Вставить("ДатаНачала",    Сертификат.ДатаНачала    + ДобавкаВремени);
	ДатыСертификата.Вставить("ДатаОкончания", Сертификат.ДатаОкончания + ДобавкаВремени);
	
	Возврат ДатыСертификата;
	
КонецФункции

// Для функции СвойстваСертификата.
Функция ПолучитьНазначение(Сертификат)
	
	Если Не Сертификат.РасширенныеСвойства.Свойство("EKU") Тогда
		Возврат "";
	КонецЕсли;
	
	ФиксированныйМассивСвойств = Сертификат.РасширенныеСвойства.EKU;
	
	Назначение = "";
	
	Для Индекс = 0 По ФиксированныйМассивСвойств.Количество() - 1 Цикл
		Назначение = Назначение + ФиксированныйМассивСвойств.Получить(Индекс);
		Назначение = Назначение + Символы.ПС;
	КонецЦикла;
	
	Возврат ПодготовитьСтроку(Назначение);
	
КонецФункции

// Возвращает сведения из свойств сертификата строкой.
//
// Параметры:
//  СвойстваСертификата - см. ЭлектроннаяПодпись.СвойстваСертификата
// 
// Возвращаемое значение:
//  Строка
//
Функция СведенияОСертификатеСтрокой(СвойстваСертификата) Экспорт
	
	СведенияОСертификате = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Сертификат: %1
			|Кем выдан: %2
			|Владелец: %3
			|Действителен: с %4 по %5'"),
		Строка(СвойстваСертификата.СерийныйНомер),
		СвойстваСертификата.КемВыдан,
		СвойстваСертификата.КомуВыдан,
		Формат(СвойстваСертификата.ДатаНачала,    "ДЛФ=D"),
		Формат(СвойстваСертификата.ДействителенДо, "ДЛФ=D"));
	
	Возврат СведенияОСертификате;
	
КонецФункции

// Для функций СвойстваСубъектаСертификата, СвойстваИздателяСертификата.
Функция ПодготовитьСтроку(СтрокаИзСертификата)
	
	Возврат СокрЛП(ОбщегоНазначенияКлиентСервер.ЗаменитьНедопустимыеСимволыXML(СтрокаИзСертификата));
	
КонецФункции

// Для функции ОбщееОписаниеОшибки.
Функция УпрощеннаяСтруктураОшибки(Ошибка, ЗаголовокОшибки)
	
	УпрощеннаяСтруктура = Новый Структура;
	УпрощеннаяСтруктура.Вставить("ОписаниеОшибки",  "");
	УпрощеннаяСтруктура.Вставить("ЗаголовокОшибки", "");
	УпрощеннаяСтруктура.Вставить("Описание",        "");
	УпрощеннаяСтруктура.Вставить("НеПоддерживается", Ложь);
	
	Если ТипЗнч(Ошибка) = Тип("Строка") Тогда
		УпрощеннаяСтруктура.ОписаниеОшибки = СокрЛП(Ошибка);
		Возврат УпрощеннаяСтруктура;
		
	ИначеЕсли ТипЗнч(Ошибка) <> Тип("Структура") Тогда
		Возврат УпрощеннаяСтруктура;
	КонецЕсли;
	
	Если Ошибка.Свойство("ОписаниеОшибки") Тогда
		УпрощеннаяСтруктура.ОписаниеОшибки = СокрЛП(Ошибка.ОписаниеОшибки);
	КонецЕсли;
	
	Если Ошибка.Свойство("ЗаголовокОшибки") Тогда
		Если Ошибка.Свойство("Ошибки") И Ошибка.Ошибки.Количество() = 1 Тогда
			Если ЗаголовокОшибки <> Неопределено Тогда
				УпрощеннаяСтруктура.ЗаголовокОшибки = Ошибка.ЗаголовокОшибки;
			КонецЕсли;
			СвойстваОшибки = Ошибка.Ошибки[0]; // см. НовыеСвойстваОшибки
			НовыеСвойстваОшибки = НовыеСвойстваОшибки();
			ЗаполнитьЗначенияСвойств(НовыеСвойстваОшибки, СвойстваОшибки);
			Описание = "";
			Если ЗначениеЗаполнено(НовыеСвойстваОшибки.Программа) Тогда
				Описание = Описание + Строка(НовыеСвойстваОшибки.Программа) + ":" + Символы.ПС;
			КонецЕсли;
			Описание = Описание + НовыеСвойстваОшибки.Описание;
			УпрощеннаяСтруктура.Описание = СокрЛП(Описание);
			УпрощеннаяСтруктура.ОписаниеОшибки = СокрЛП(УпрощеннаяСтруктура.ЗаголовокОшибки + Символы.ПС + Описание);
			Если НовыеСвойстваОшибки.НеПоддерживается Тогда
				УпрощеннаяСтруктура.НеПоддерживается = Истина;
			КонецЕсли;
		КонецЕсли;
	ИначеЕсли ЗначениеЗаполнено(ЗаголовокОшибки) Тогда
		УпрощеннаяСтруктура.ЗаголовокОшибки = ЗаголовокОшибки;
		УпрощеннаяСтруктура.Описание = УпрощеннаяСтруктура.ОписаниеОшибки;
		УпрощеннаяСтруктура.ОписаниеОшибки = ЗаголовокОшибки
			+ Символы.ПС + УпрощеннаяСтруктура.ОписаниеОшибки;
	КонецЕсли;
	
	Возврат УпрощеннаяСтруктура;
	
КонецФункции

// Возвращает информацию об используемом компьютере.
//
// Возвращаемое значение:
//   Строка - информация о компьютере.
//
Функция ДиагностическаяИнформацияОКомпьютере(ДляКлиента = Ложь) Экспорт
	
	СисИнфо = Новый СистемнаяИнформация;
	ПрограммаПросмотра = ?(ДляКлиента, СисИнфо.ИнформацияПрограммыПросмотра, "");
	
	Если Не ПустаяСтрока(ПрограммаПросмотра) Тогда
		ПрограммаПросмотра = Символы.ПС + НСтр("ru = 'Программа просмотра:'") + " " + ПрограммаПросмотра;
	КонецЕсли;
	
	Возврат НСтр("ru = 'Операционная система:'") + " " + СисИнфо.ВерсияОС
		+ Символы.ПС + НСтр("ru = 'Версия приложения:'") + " " + СисИнфо.ВерсияПриложения
		+ Символы.ПС + НСтр("ru = 'Тип платформы:'") + " " + СисИнфо.ТипПлатформы
		+ ПрограммаПросмотра;
	
КонецФункции

Функция ДиагностическаяИнформацияПоПрограмме(Программа, МенеджерКриптографии, ОписаниеОшибки) Экспорт
	
	Если ТипЗнч(МенеджерКриптографии) = Тип("МенеджерКриптографии") Тогда
		Результат = НСтр("ru = 'ОК'");
	Иначе
		ТекстОшибки = "";
		Если ТипЗнч(ОписаниеОшибки) = Тип("Структура")
		   И ОписаниеОшибки.Свойство("Ошибки")
		   И ТипЗнч(ОписаниеОшибки.Ошибки) = Тип("Массив")
		   И ОписаниеОшибки.Ошибки.Количество() > 0 Тогда
			
			Ошибка = ОписаниеОшибки.Ошибки[0]; // см. НовыеСвойстваОшибки
			ТекстОшибки = Ошибка.Описание;
		КонецЕсли;
		Результат = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка ""%1""'"), ТекстОшибки);
	КонецЕсли;
	
	Возврат Программа.Представление + " - " + Результат + Символы.ПС;
	
КонецФункции

// Только для внутреннего использования.
// 
// Параметры:
//  Криптопровайдер - Соответствие - из ответа компоненты
//  ПрограммыПоИменамСТипом - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОбщиеНастройки
//  ПроверкаНаКлиенте - Булево - криптопровайдер установлен на клиентском компьютере
// 
// Возвращаемое значение:
//   см. НовоеРасширенноеОписаниеПрограммы
//
Функция РасширенноеОписаниеПрограммы(Криптопровайдер, ПрограммыПоИменамСТипом, ПроверкаНаКлиенте = Истина) Экспорт
	
	ОписаниеПрограммы = НовоеРасширенноеОписаниеПрограммы();
	
	ТипПрограммы = Криптопровайдер.Получить("type");
	Если ТипПрограммы = 0 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ОписаниеПрограммы.ТипПрограммы = ТипПрограммы;
	ОписаниеПрограммы.ИмяПрограммы = Криптопровайдер.Получить("name");
	
	Ключ = КлючПоискаПрограммыПоИмениСТипом(ОписаниеПрограммы.ИмяПрограммы, ОписаниеПрограммы.ТипПрограммы);
	ПоставляемаяПрограмма = ПрограммыПоИменамСТипом.Получить(Ключ);
	
	Если ПоставляемаяПрограмма = Неопределено Тогда
		Возврат Неопределено;
	Иначе
		ЗаполнитьЗначенияСвойств(ОписаниеПрограммы, ПоставляемаяПрограмма);
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ОписаниеПрограммы.Представление) Тогда
		ОписаниеПрограммы.Представление = Ключ;
	КонецЕсли;
	
	Если ПроверкаНаКлиенте Тогда
		ОписаниеПрограммы.ПутьКПрограммеАвто = Криптопровайдер.Получить("path");
	Иначе
		ОписаниеПрограммы.ПутьКПрограммеНаСервереАвто = Криптопровайдер.Получить("path");
	КонецЕсли;
	
	ОписаниеПрограммы.Версия = Криптопровайдер.Получить("version");
	ОписаниеПрограммы.Лицензия =  Криптопровайдер.Получить("license");
	ОписаниеПрограммы.Автоопределение = Истина;
	
	Возврат ОписаниеПрограммы;
	
КонецФункции

// Только для внутреннего использования.
Процедура ОбработатьРезультатПроверкиПрограмм(Криптопровайдеры, Программы, ВозможенКонфликт, Контекст, ЕстьПроверяемыеПрограммы = Ложь) Экспорт
	
	УстановленныеПрограммы = Новый Соответствие;
	
	Для Каждого ТекущийКриптопровайдер Из Криптопровайдеры Цикл
		
		Найдена = Истина; Представление = Неопределено;
		
		Если ЗначениеЗаполнено(Контекст.АлгоритмыПодписи) Тогда
			Найдена = Ложь;
			Для Каждого Алгоритм Из Контекст.АлгоритмыПодписи Цикл
				Найдена = МенеджерКриптографииАлгоритмПодписиПоддерживается(ТекущийКриптопровайдер,
					?(Контекст.ТипДанных = "Сертификат","","ПроверкаПодписи"), Алгоритм, Неопределено, Контекст.ЭтоСервер, Ложь);
				Если Найдена Тогда
					Прервать;
				КонецЕсли;
			КонецЦикла;
		ИначеЕсли ЗначениеЗаполнено(Контекст.ПроверяемыеПрограммы) Тогда
			Найдена = Ложь;
			Для Каждого Программа Из Контекст.ПроверяемыеПрограммы Цикл
				Если Программа.ИмяПрограммы = ТекущийКриптопровайдер.ИмяПрограммы
					И Программа.ТипПрограммы = ТекущийКриптопровайдер.ТипПрограммы Тогда
					Представление = Программа.Представление;
					Найдена = Истина;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		Если Не Найдена Тогда
			Продолжить;
		КонецЕсли;
		
		Если Контекст.РасширенноеОписание Тогда
			РезультатПроверкиПрограммы = НовоеРасширенноеОписаниеПрограммы();
		Иначе
			РезультатПроверкиПрограммы = РезультатПроверкиПрограммы();
		КонецЕсли;
		
		ЗаполнитьЗначенияСвойств(РезультатПроверкиПрограммы, ТекущийКриптопровайдер);
		РезультатПроверкиПрограммы.Представление = 
			?(ЗначениеЗаполнено(Представление), Представление, ТекущийКриптопровайдер.Представление);
		
		РезультатПроверкиПрограммы.Вставить("Программа", ПрограммаЭлектроннойПодписи(ТекущийКриптопровайдер));
		Если Не ПустаяСтрока(РезультатПроверкиПрограммы.Программа) Тогда
			УстановленныеПрограммы.Вставить(РезультатПроверкиПрограммы.Программа, Истина);
		КонецЕсли;
		
		Программы.Добавить(РезультатПроверкиПрограммы);
		
	КонецЦикла;
	
	Если УстановленныеПрограммы.Количество() > 0 Тогда
		ЕстьПроверяемыеПрограммы = Истина;
		ВозможенКонфликт = УстановленныеПрограммы.Количество() > 1;
	КонецЕсли;
	
КонецПроцедуры

Функция ПрограммаЭлектроннойПодписи(Криптопровайдер)
	
	// Локализация
	
	Если ПустаяСтрока(Криптопровайдер.Идентификатор) Тогда
		Возврат "";
	КонецЕсли;
	
	Если СтрНайти(Криптопровайдер.Идентификатор, "CryptoPro") <> 0 Тогда
		Возврат НСтр("ru = 'КриптоПро CSP'");
	КонецЕсли;
	
	Если СтрНайти(Криптопровайдер.Идентификатор, "VipNet") <> 0 Тогда
		Возврат НСтр("ru = 'ViPNet CSP'");
	КонецЕсли;
	
	Если СтрНайти(Криптопровайдер.Идентификатор, "Lissi") <> 0 Тогда
		Возврат НСтр("ru = 'ЛИССИ CSP'");
	КонецЕсли;
	
	Если СтрНайти(Криптопровайдер.Идентификатор, "SignalCom") <> 0 Тогда
		Возврат НСтр("ru = 'Сигнал-КОМ CSP'");
	КонецЕсли;
	
	// Конец Локализация
	
	Возврат "";
	
КонецФункции

// Только для внутреннего использования.
Функция РезультатПроверкиПрограммы()
	
	Структура = Новый Структура;
	Структура.Вставить("Представление");
	Структура.Вставить("Ссылка");
	Структура.Вставить("ИмяПрограммы");
	Структура.Вставить("ТипПрограммы");
	Структура.Вставить("Программа");
	Структура.Вставить("Версия");
	Структура.Вставить("Лицензия");

	Возврат Структура;
	
КонецФункции

// Только для внутреннего использования.
Функция РазмещениеСертификата(ТипРазмещения) Экспорт
	
	Результат = "Локальный";
	ОбщееРазмещение = (ТипРазмещения - 1) % 4;
	
	Если ОбщееРазмещение = 2 Тогда
		Результат = "ОблачнаяПодпись";
	ИначеЕсли ОбщееРазмещение = 3 Тогда
		Результат = "ПодписьВМоделиСервиса";
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * Ссылка - Неопределено, СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования
//   * Представление - Строка
//   * ИмяПрограммы - Строка
//   * ТипПрограммы - Число
//   * АлгоритмПодписи - Строка
//   * АлгоритмХеширования - Строка
//   * АлгоритмШифрования - Строка
//   * Идентификатор - Строка
//   * ПутьКПрограмме - Строка
//   * ПутьКПрограммеАвто - Строка
//   * ПутьКПрограммеНаСервереАвто - Строка
//   * Версия - Строка - версия библиотеки
//   * Лицензия - Булево - есть ли лицензия к программе
//   * РежимИспользования - ПеречислениеСсылка.РежимыИспользованияПрограммыЭлектроннойПодписи
//   * Автоопределение - Булево - программа определена автоматически
//
Функция НовоеРасширенноеОписаниеПрограммы() Экспорт
	
	Описание = Новый Структура;
	Описание.Вставить("Ссылка");
	Описание.Вставить("Представление");
	Описание.Вставить("ИмяПрограммы");
	Описание.Вставить("ТипПрограммы");
	Описание.Вставить("АлгоритмПодписи");
	Описание.Вставить("АлгоритмХеширования");
	Описание.Вставить("АлгоритмШифрования");
	Описание.Вставить("Идентификатор");
	Описание.Вставить("АлгоритмыПроверкиПодписи");
	
	Описание.Вставить("ПутьКПрограммеАвто", "");
	Описание.Вставить("ПутьКПрограммеНаСервереАвто", "");
	Описание.Вставить("Версия");
	Описание.Вставить("Лицензия", Ложь);
	Описание.Вставить("РежимИспользования", ПредопределенноеЗначение(
		"Перечисление.РежимыИспользованияПрограммыЭлектроннойПодписи.Автоматически"));
	Описание.Вставить("Автоопределение", Истина);
	
	Возврат Описание;

КонецФункции

#Область ОбластьXML

// Параметры:
//  СтрокаXML   - Строка
//  ИмяЭлемента - Строка
//
// Возвращаемое значение:
//   см. СвойстваОбластиXML
//
Функция ОбластьXML(СтрокаXML, ИмяЭлемента, НомерSignature = 1) Экспорт
	
	Результат = СвойстваОбластиXML(ИмяЭлемента);
	
	// Требуется отделять имя элемента, так как иначе в документе могут встречаться элементы, которые начинаются с того же
	// набора символов.
	ПризнакНачалаОбласти = "<" + ИмяЭлемента + " ";
	ПризнакОкончанияОбласти = "</" + ИмяЭлемента + ">";
	
	Позиция = СтрНайти(СтрокаXML, ПризнакНачалаОбласти, , , НомерSignature);
	Если Позиция = 0 Тогда
		// Если элемент не нашли, то попробуем убрать пробел.
		ПризнакНачалаОбласти = "<" + ИмяЭлемента;
		Позиция = СтрНайти(СтрокаXML, ПризнакНачалаОбласти, , , НомерSignature);
		Если Позиция = 0 Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не найдена элемент ""%1"" в документе XML.'"), ИмяЭлемента);
			Результат.ТекстОшибки = ТекстОшибки;
		КонецЕсли;
	КонецЕсли;
	Результат.ПозицияНачала = Позиция;
	Текст = Сред(СтрокаXML, Позиция);
	
	НомерВхождения = 1;
	Позиция = СтрНайти(Текст, ПризнакНачалаОбласти, , 2, НомерВхождения);
	Пока Позиция <> 0 Цикл
		Позиция = СтрНайти(Текст, ПризнакНачалаОбласти, , 2, НомерВхождения);
		НомерВхождения = НомерВхождения + 1;
	КонецЦикла;
	
	Позиция = СтрНайти(Текст, ПризнакОкончанияОбласти);
	Если Позиция = 0 Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не найдено окончание элемента ""%1"" в документе XML.'"), ИмяЭлемента);
		Результат.ТекстОшибки = ТекстОшибки;
	КонецЕсли;
	
	ПозицияСледующейОбласти = Позиция + СтрДлина(ПризнакОкончанияОбласти);
	Результат.Текст = Сред(Текст, 1, ПозицияСледующейОбласти - 1);
	Результат.ПозицияСледующейОбласти = Результат.ПозицияНачала + ПозицияСледующейОбласти;
	
	Текст = Сред(Текст, 1, Позиция - 1);
	
	Позиция = СтрНайти(Текст, ">");
	Если Позиция = 0 Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не найдено окончание заголовка элемента ""%1"" в документе XML.'"), ИмяЭлемента);
		Результат.ТекстОшибки = ТекстОшибки;
	КонецЕсли;
	
	Результат.Начало = Сред(Текст, 1, Позиция);
	Результат.Конец  = ПризнакОкончанияОбласти;
	Результат.Содержимое = Сред(Текст, Позиция + 1);
	
	Возврат Результат;

КонецФункции

// Параметры:
//  ОбластьXML - см. СвойстваОбластиXML
//  Начало     - Неопределено
//             - Строка
//
// Возвращаемое значение:
//  Строка
//
Функция ТекстОбластиXML(ОбластьXML, Начало = Неопределено) Экспорт
	
	ЧастиТекста = Новый Массив;
	ЧастиТекста.Добавить(?(Начало = Неопределено, ОбластьXML.Начало, Начало));
	ЧастиТекста.Добавить(ОбластьXML.Содержимое);
	ЧастиТекста.Добавить(ОбластьXML.Конец);
	Результат = СтрСоединить(ЧастиТекста);
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  ОбластьXML - см. СвойстваОбластиXML
//  Алгоритм   - см. ЭлектроннаяПодписьСлужебный.АлгоритмКанонизации
//  ТекстXML   - Строка
//
// Возвращаемое значение:
//  Строка
//
Функция РасширенноеНачалоОбластиXML(ОбластьXML, Алгоритм, ТекстXML) Экспорт
	
	Результат = Новый Структура("Начало, ТекстОшибки", , "");
	
	Если Алгоритм.Вид = "c14n"
	 Или Алгоритм.Вид = "smev" Тогда
		
		Если ТекстXML = Неопределено Тогда
			ТекущаяОбластьXML = ОбластьXML;
		Иначе
			ТекущаяОбластьXML = ОбластьXML(ТекстXML, ОбластьXML.ИмяЭлемента);
			Если ЗначениеЗаполнено(ТекущаяОбластьXML.ТекстОшибки) Тогда
				Результат.ТекстОшибки = ТекущаяОбластьXML.ТекстОшибки;
				Возврат Результат;
			КонецЕсли;
			ТекущаяОбластьXML.ПространстваИменДоУзла = ОбластьXML.ПространстваИменДоУзла;
		КонецЕсли;
		Результат.Начало = РасширенноеНачало(ТекущаяОбластьXML);
	Иначе
		Результат.Начало = ОбластьXML.Начало;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  ОбластьXML - см. СвойстваОбластиXML
//
// Возвращаемое значение:
//  Строка
//
Функция РасширенноеНачало(ОбластьXML)
	
	Если Не ЗначениеЗаполнено(ОбластьXML.ПространстваИменДоУзла) Тогда
		Возврат ОбластьXML.Начало;
	КонецЕсли;
	
	Дополнительные = Новый Массив;
	Для Каждого ИмяПространства Из ОбластьXML.ПространстваИменДоУзла Цикл
		Позиция = СтрНайти(ИмяПространства, "=""");
		ОбъявлениеПространства = Лев(ИмяПространства, Позиция + 1);
		Если СтрНайти(ОбластьXML.Начало, ОбъявлениеПространства) > 0 Тогда
			Продолжить;
		КонецЕсли;
		Дополнительные.Добавить(ИмяПространства);
	КонецЦикла;
	
	Результат = Лев(ОбластьXML.Начало, СтрДлина(ОбластьXML.ИмяЭлемента) + 1)
		+ " " + СтрСоединить(Дополнительные, " ")
		+ " " + Сред(ОбластьXML.Начало, СтрДлина(ОбластьXML.ИмяЭлемента) + 2);
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  ИмяЭлемента - Строка
//
// Возвращаемое значение:
//  Структура:
//   * ИмяЭлемента - Строка
//   * ТекстОшибки - Строка
//   * ПозицияНачала - Число
//   * ПозицияСледующейОбласти - Число
//   * Начало      - Строка
//   * Содержимое - Строка
//   * Конец - Строка
//   * ПространстваИменДоУзла - Массив из Строка
//                            - Неопределено
//
Функция СвойстваОбластиXML(ИмяЭлемента)
	
	Результат = Новый Структура;
	Результат.Вставить("ИмяЭлемента", ИмяЭлемента);
	Результат.Вставить("ТекстОшибки", "");
	Результат.Вставить("ПозицияНачала", 0);
	Результат.Вставить("ПозицияСледующейОбласти", 0);
	Результат.Вставить("Начало", "");
	Результат.Вставить("Содержимое", "");
	Результат.Вставить("Конец", "");
	Результат.Вставить("Текст", "");
	Результат.Вставить("ПространстваИменДоУзла");
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#Область СодержимоеСертификата

Функция ДополнительныеСвойстваСертификата(Данные, ДобавкаВремени = Неопределено) Экспорт

	Структура = Новый Структура;
	Структура.Вставить("ТекстОшибки", "");
	Структура.Вставить("ДатаНачалаЗакрытогоКлюча", Дата(1,1,1));
	Структура.Вставить("ДатаОкончанияЗакрытогоКлюча", Дата(1,1,1));
	Структура.Вставить("ИдентификаторКлючаУдостоверяющегоЦентра", "");
	Структура.Вставить("СодержитВстроеннуюЛицензиюКриптоПро", Ложь);
	
	ДвоичныеДанные = ДвоичныеДанныеИзДанных(Данные,
		"ЭлектроннаяПодписьСлужебныйКлиентСервер.ДополнительныеСвойстваСертификата");
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
	
	//	TBSCertificate  ::=  SEQUENCE  {
	//		version			[0] EXPLICIT Version DEFAULT v1,
	//		...
	//		extensions		[3] EXPLICIT Extensions OPTIONAL
	//							 -- If present, version MUST be v3
	
	// SEQUENCE (Certificate).
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
		// SEQUENCE (tbsCertificate).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);
			// [0] EXPLICIT (version).
			ПропуститьНачалоБлока(АнализДанных, 2, 0);
				// INTEGER {v1(0), v2(1), v3(2)}. 
				ПропуститьНачалоБлока(АнализДанных, 0, 2); 
				Целое = ПрочитатьПотоковоеЦелое(АнализДанных);
				Если Целое <> 2 Тогда
					Структура.ТекстОшибки = НСтр("ru = 'Данные не являются сертификатом.'");
					Возврат Структура;
				КонецЕсли;
				ПропуститьРодительскийБлок(АнализДанных);
			// version
			ПропуститьРодительскийБлок(АнализДанных);
			// INTEGER  (serialNumber         CertificateSerialNumber).
			ПропуститьБлок(АнализДанных, 0, 2);
			// SEQUENCE (signature            AlgorithmIdentifier).
			ПропуститьБлок(АнализДанных, 0, 16);
			// SEQUENCE (issuer               Name).
			ПропуститьБлок(АнализДанных, 0, 16);
			// SEQUENCE (validity             Validity).
			ПропуститьБлок(АнализДанных, 0, 16);
			// SEQUENCE (subject              Name).
			ПропуститьБлок(АнализДанных, 0, 16);
			// SEQUENCE (subjectPublicKeyInfo SubjectPublicKeyInfo).
			ПропуститьБлок(АнализДанных, 0, 16);
			// [1] IMPLICIT UniqueIdentifier OPTIONAL (issuerUniqueID).
			ПропуститьБлок(АнализДанных, 2, 1, Ложь);
			// [2] IMPLICIT UniqueIdentifier OPTIONAL (subjectUniqueID).
			ПропуститьБлок(АнализДанных, 2, 2, Ложь);
			// [3] EXPLICIT SEQUENCE SIZE (1..MAX) OF Extension (extensions). 
			ПропуститьНачалоБлока(АнализДанных, 2, 3);
			Если АнализДанных.ЕстьОшибка Тогда
				Структура.ТекстОшибки = НСтр("ru = 'Ошибка в данных сертификата.'");
				Возврат Структура;
			КонецЕсли; 
				// SEQUENCE OF
				ПропуститьНачалоБлока(АнализДанных, 0, 16);
				СмещениеСледующего = АнализДанных.Родители[0].СмещениеСледующего;
				Пока АнализДанных.Смещение < СмещениеСледующего Цикл
					// SEQUENCE (extension).
					ПропуститьНачалоБлока(АнализДанных, 0, 16);
					Если АнализДанных.ЕстьОшибка Тогда
						Структура.ТекстОшибки = НСтр("ru = 'Ошибка в данных сертификата.'");
						Возврат Структура;
					КонецЕсли; 
						// OBJECT IDENTIFIER
						ПропуститьНачалоБлока(АнализДанных, 0, 6);
							
						РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
						Если РазмерДанных = 0 Тогда
							Структура.ТекстОшибки = НСтр("ru = 'Ошибка в данных сертификата.'");
							Возврат Структура;
						КонецЕсли;
						
						Если РазмерДанных = 3 Тогда
							Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
							СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
							ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER
							Если СтрокаБуфера = "551D23" Тогда // 2.5.29.35 authorityKeyIdentifier
								ЗаполнитьИдентификаторКлючаУдостоверяющегоЦентра(ПрочитанныйБлок(АнализДанных, 0, 4, Истина), Структура);
							ИначеЕсли СтрокаБуфера = "551D10" Тогда // 2.5.29.16 privateKeyUsagePeriod
								ЗаполнитьПериодДействияЗакрытогоКлюча(ПрочитанныйБлок(АнализДанных, 0, 4, Истина), Структура, ДобавкаВремени);
							КонецЕсли;
						
						ИначеЕсли РазмерДанных = 7 Тогда
							Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
							СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
							Если СтрокаБуфера = "2A850302023102"  Тогда // 1.2.643.2.2.49.2
								Структура.СодержитВстроеннуюЛицензиюКриптоПро = Истина;
							КонецЕсли;
							ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER
						Иначе
							ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER
						КонецЕсли;
						
					ПропуститьРодительскийБлок(АнализДанных); // SEQUENCE
				КонецЦикла;
	Возврат Структура;

КонецФункции

Процедура ЗаполнитьПериодДействияЗакрытогоКлюча(ДвоичныеДанные, Структура, ДобавкаВремени)
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
	
	// OCTET STRING
	ПропуститьНачалоБлока(АнализДанных, 0, 4);
	
	// PrivateKeyUsagePeriod ::= SEQUENCE {
	// notBefore       [0]     GeneralizedTime OPTIONAL,
	// notAfter        [1]     GeneralizedTime OPTIONAL }
	// SEQUENCE
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
	
	// [0]
	Если ПропуститьНачалоБлока(АнализДанных, 2, 0, Ложь) Тогда
		БуферДаты = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, 14);
		ПредставлениеДаты = ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДаты);
		ОписаниеТипа = Новый ОписаниеТипов("Дата");
		Структура.ДатаНачалаЗакрытогоКлюча = ОписаниеТипа.ПривестиЗначение(ПредставлениеДаты);
		ПропуститьРодительскийБлок(АнализДанных); // [0]
	КонецЕсли;
	
	// [1]
	Если ПропуститьНачалоБлока(АнализДанных, 2, 1, Ложь) Тогда
		БуферДаты = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, 14);
		ПредставлениеДаты = ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДаты);
		ОписаниеТипа = Новый ОписаниеТипов("Дата");
		Структура.ДатаОкончанияЗакрытогоКлюча = ОписаниеТипа.ПривестиЗначение(ПредставлениеДаты);
		ПропуститьРодительскийБлок(АнализДанных); // [1]
	КонецЕсли;
	
	Если ДобавкаВремени <> Неопределено И ЗначениеЗаполнено(Структура.ДатаНачалаЗакрытогоКлюча) Тогда
		Структура.ДатаНачалаЗакрытогоКлюча = Структура.ДатаНачалаЗакрытогоКлюча + ДобавкаВремени;
	КонецЕсли;
	
	Если ДобавкаВремени <> Неопределено И ЗначениеЗаполнено(Структура.ДатаОкончанияЗакрытогоКлюча) Тогда
		Структура.ДатаОкончанияЗакрытогоКлюча = Структура.ДатаОкончанияЗакрытогоКлюча + ДобавкаВремени;
	КонецЕсли;

КонецПроцедуры

Процедура ЗаполнитьИдентификаторКлючаУдостоверяющегоЦентра(ДвоичныеДанные, Структура)
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
	// OCTET STRING
	ПропуститьНачалоБлока(АнализДанных, 0, 4);

	// AuthorityKeyIdentifier ::= SEQUENCE {
	//      keyIdentifier             [0] KeyIdentifier           OPTIONAL,
	//
	//   KeyIdentifier ::= OCTET STRING

	// SEQUENCE
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
	// [0]
	ПропуститьНачалоБлока(АнализДанных, 2, 0);

	Если Не АнализДанных.ЕстьОшибка Тогда
		РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
		Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
		Структура.ИдентификаторКлючаУдостоверяющегоЦентра = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
	КонецЕсли; 

КонецПроцедуры

Функция АлгоритмСформированнойПодписи(ДанныеПодписи, ВключаяOID = Ложь, ТолькоOID = Ложь, АлгоритмПодписиНеСоответствуетГОСТ = Неопределено) Экспорт
	
	Возврат АлгоритмПодписи(ДанныеПодписи, Ложь, ВключаяOID, ТолькоOID, АлгоритмПодписиНеСоответствуетГОСТ);
	
КонецФункции

Функция АлгоритмПодписиСертификата(ДанныеСертификата, ВключаяOID = Ложь, ТолькоOID = Ложь) Экспорт
	
	Возврат АлгоритмПодписи(ДанныеСертификата, Истина, ВключаяOID, ТолькоOID);
	
КонецФункции

Функция АлгоритмПодписи(Данные, ЭтоДанныеСертификата, ВключаяOID = Ложь, ТолькоOID = Ложь, АлгоритмПодписиНеСоответствуетГОСТ = Неопределено)
	
	ДвоичныеДанные = ДвоичныеДанныеИзДанных(Данные,
		"ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмПодписи");
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
	OIDАлгоритмаХеширования = "";
	
	Если ЭтоДанныеСертификата Тогда
		// SEQUENCE (Certificate).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);
			// SEQUENCE (tbsCertificate).
			ПропуститьНачалоБлока(АнализДанных, 0, 16);
				//          (version              [0]  EXPLICIT Version DEFAULT v1).
				ПропуститьБлок(АнализДанных, 2, 0);
				// INTEGER  (serialNumber         CertificateSerialNumber).
				ПропуститьБлок(АнализДанных, 0, 2);
				// SEQUENCE (signature            AlgorithmIdentifier).
				ПропуститьБлок(АнализДанных, 0, 16);
				// SEQUENCE (issuer               Name).
				ПропуститьБлок(АнализДанных, 0, 16);
				// SEQUENCE (validity             Validity).
				ПропуститьБлок(АнализДанных, 0, 16);
				// SEQUENCE (subject              Name).
				ПропуститьБлок(АнализДанных, 0, 16);
				// SEQUENCE (subjectPublicKeyInfo SubjectPublicKeyInfo).
				ПропуститьНачалоБлока(АнализДанных, 0, 16);
					// SEQUENCE (algorithm  AlgorithmIdentifier).
					ПропуститьНачалоБлока(АнализДанных, 0, 16);
						// OBJECT IDENTIFIER (algorithm).
						ПропуститьНачалоБлока(АнализДанных, 0, 6);
	Иначе
		
		// SEQUENCE (PKCS #7 ContentInfo).
		ПропуститьНачалоБлока(АнализДанных, 0, 16);
			// OBJECT IDENTIFIER (contentType).
			ПропуститьНачалоБлока(АнализДанных, 0, 6);
				// 1.2.840.113549.1.7.2 signedData (PKCS #7).
				ПроверитьДанныеБлока(АнализДанных, "2A864886F70D010702");
				ПропуститьРодительскийБлок(АнализДанных);
			// [0]CS             (content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL).
			ПропуститьНачалоБлока(АнализДанных, 2, 0);
				// SEQUENCE (content SignedData).
				ПропуститьНачалоБлока(АнализДанных, 0, 16);
					// INTEGER  (version          Version).
					ПропуститьБлок(АнализДанных, 0, 2);
					// SET      (digestAlgorithms DigestAlgorithmIdentifiers).
					ПропуститьБлок(АнализДанных, 0, 17);
					// SEQUENCE (contentInfo      ContentInfo).
					ПропуститьБлок(АнализДанных, 0, 16);
					// [0]CS    (certificates     [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL).
					ПропуститьБлок(АнализДанных, 2, 0, Ложь);
					// [1]CS    (crls             [1] IMPLICIT CertificateRevocationLists OPTIONAL).
					ПропуститьБлок(АнализДанных, 2, 1, Ложь);
					// SET      (signerInfos      SET OF SignerInfo).
					ПропуститьНачалоБлока(АнализДанных, 0, 17);
						// SEQUENCE (signerInfo SignerInfo).
						ПропуститьНачалоБлока(АнализДанных, 0, 16);
							// INTEGER  (version                   Version).
							ПропуститьБлок(АнализДанных, 0, 2);
							// SEQUENCE (issuerAndSerialNumber     IssuerAndSerialNumber).
							ПропуститьБлок(АнализДанных, 0, 16);
								// SEQUENCE (digestAlgorithm           DigestAlgorithmIdentifier).
								ПропуститьНачалоБлока(АнализДанных, 0, 16);
									// OBJECT IDENTIFIER (algorithm).
									ПропуститьНачалоБлока(АнализДанных, 0, 6);
									OIDАлгоритмаХеширования = ПрочитанныйOID(АнализДанных);
									ПропуститьРодительскийБлок(АнализДанных); // OBJECT IDENTIFIER (algorithm)
								ПропуститьРодительскийБлок(АнализДанных); // SEQUENCE (digestAlgorithm           DigestAlgorithmIdentifier).
							// [0]CS    (authenticatedAttributes   [0] IMPLICIT Attributes OPTIONAL).
							ПропуститьБлок(АнализДанных, 2, 0, Ложь);
							// SEQUENCE (digestEncryptionAlgorithm AlgorithmIdentifier).
							ПропуститьНачалоБлока(АнализДанных, 0, 16);
								// OBJECT IDENTIFIER (algorithm).
								ПропуститьНачалоБлока(АнализДанных, 0, 6);
	КонецЕсли;
	
	OIDАлгоритмаПодписи = ПрочитанныйOID(АнализДанных);
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат "";
	КонецЕсли;
	
	СоответствующийOIDПодписи = АлгоритмПодписиСоответствующийАлгоритмуХешированияГОСТ(OIDАлгоритмаХеширования);
	Если СоответствующийOIDПодписи <> Неопределено И OIDАлгоритмаПодписи <> СоответствующийOIDПодписи Тогда
		
		ИдентификаторыАлгоритмов = ИдентификаторыАлгоритмовПодписи(Ложь);
		АлгоритмНеправильный = АлгоритмПоOID(OIDАлгоритмаПодписи, ИдентификаторыАлгоритмов, Истина);
		АлгоритмПравильный = АлгоритмПоOID(СоответствующийOIDПодписи, ИдентификаторыАлгоритмов, Истина);
		
		ИдентификаторыАлгоритмов = ИдентификаторыАлгоритмовХеширования();
		АлгоритмХеширования = АлгоритмПоOID(OIDАлгоритмаХеширования, ИдентификаторыАлгоритмов, Истина);
		
		АлгоритмПодписиНеСоответствуетГОСТ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Алгоритм %1, указанный в подписи, не соответствует стандарту, так как должен быть указан %2, соответствующий алгоритму хеширования %3.'"),
			АлгоритмНеправильный, АлгоритмПравильный, АлгоритмХеширования);
		OIDАлгоритмаПодписи = СоответствующийOIDПодписи;
	КонецЕсли;
	
	Если ТолькоOID Тогда
		Возврат OIDАлгоритмаПодписи;
	КонецЕсли;
	
	ИдентификаторыАлгоритмов = ИдентификаторыАлгоритмовПодписи(ЭтоДанныеСертификата);
	Алгоритм = АлгоритмПоOID(OIDАлгоритмаПодписи, ИдентификаторыАлгоритмов, ВключаяOID);
	
	Возврат Алгоритм;
	
КонецФункции

Функция АлгоритмХеширования(Данные, ВключаяOID = Ложь) Экспорт
	
	ДвоичныеДанные = ДвоичныеДанныеИзДанных(Данные,
		"ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмХеширования");
	
	АнализДанных = НовыйАнализДанных(ДвоичныеДанные);
	
	// SEQUENCE (PKCS #7 ContentInfo).
	ПропуститьНачалоБлока(АнализДанных, 0, 16);
		// OBJECT IDENTIFIER (contentType).
		ПропуститьНачалоБлока(АнализДанных, 0, 6);
			// 1.2.840.113549.1.7.2 signedData (PKCS #7).
			ПроверитьДанныеБлока(АнализДанных, "2A864886F70D010702");
			ПропуститьРодительскийБлок(АнализДанных);
		// [0]CS             (content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL).
		ПропуститьНачалоБлока(АнализДанных, 2, 0);
			// SEQUENCE (content SignedData).
			ПропуститьНачалоБлока(АнализДанных, 0, 16);
				// INTEGER  (version          Version).
				ПропуститьБлок(АнализДанных, 0, 2);
				// SET      (digestAlgorithms DigestAlgorithmIdentifiers).
				ПропуститьБлок(АнализДанных, 0, 17);
				// SEQUENCE (contentInfo      ContentInfo).
				ПропуститьБлок(АнализДанных, 0, 16);
				// [0]CS    (certificates     [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL).
				ПропуститьБлок(АнализДанных, 2, 0, Ложь);
				// [1]CS    (crls             [1] IMPLICIT CertificateRevocationLists OPTIONAL).
				ПропуститьБлок(АнализДанных, 2, 1, Ложь);
				// SET      (signerInfos      SET OF SignerInfo).
				ПропуститьНачалоБлока(АнализДанных, 0, 17);
					// SEQUENCE (signerInfo SignerInfo).
					ПропуститьНачалоБлока(АнализДанных, 0, 16);
						// INTEGER  (version                   Version).
						ПропуститьБлок(АнализДанных, 0, 2);
						// SEQUENCE (issuerAndSerialNumber     IssuerAndSerialNumber).
						ПропуститьБлок(АнализДанных, 0, 16);
						// SEQUENCE (digestAlgorithm           DigestAlgorithmIdentifier).
						ПропуститьНачалоБлока(АнализДанных, 0, 16);
							// OBJECT IDENTIFIER (algorithm).
							ПропуститьНачалоБлока(АнализДанных, 0, 6);
	
	OIDАлгоритмаХеширования = ПрочитанныйOID(АнализДанных);
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат "";
	КонецЕсли;
	
	ИдентификаторыАлгоритмов = ИдентификаторыАлгоритмовХеширования();
	Алгоритм = АлгоритмПоOID(OIDАлгоритмаХеширования, ИдентификаторыАлгоритмов, ВключаяOID);
	
	Возврат Алгоритм;
	
КонецФункции

Функция ДвоичныеДанныеИзДанных(Данные, ИмяФункции) Экспорт
	
	ОжидаемыеТипы = Новый Массив;
	ОжидаемыеТипы.Добавить(Тип("ДвоичныеДанные"));
	ОжидаемыеТипы.Добавить(Тип("Строка"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(
		ИмяФункции,
		"Данные", Данные, ОжидаемыеТипы);
	
	Если ТипЗнч(Данные) = Тип("Строка") Тогда
		Если ЭтоАдресВременногоХранилища(Данные) Тогда
			ДвоичныеДанные = ПолучитьИзВременногоХранилища(Данные);
		Иначе
			ОбщегоНазначенияКлиентСервер.Проверить(Ложь,
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Недопустимый адрес временного хранилища в параметре Данные:
					           |%1'") + Символы.ПС, Данные),
				ИмяФункции);
		КонецЕсли;
		Если ТипЗнч(ДвоичныеДанные) <> Тип("ДвоичныеДанные") Тогда
			ОбщегоНазначенияКлиентСервер.Проверить(Ложь,
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Недопустимый тип значения ""%1""
					           |по адресу временного хранилища, указанному в параметре Данные'") + Символы.ПС,
					Строка(ТипЗнч(ДвоичныеДанные))),
				ИмяФункции);
		КонецЕсли;
	Иначе
		ДвоичныеДанные = Данные;
	КонецЕсли;
	
	Возврат ДвоичныеДанные;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * ЕстьОшибка - Булево
//   * ЭтоОшибкаКодированияASN1 - Булево
//   * ЭтоОшибкаСтруктурыДанных - Булево
//   * Смещение - Число
//   * Родители - Массив из Структура
//   * Буфер - БуферДвоичныхДанных
// 
Функция НовыйАнализДанных(ДвоичныеДанные) Экспорт
	
	АнализДанных = Новый Структура;
	АнализДанных.Вставить("ЕстьОшибка", Ложь);
	АнализДанных.Вставить("ЭтоОшибкаКодированияASN1", Ложь); // Возможно данные повреждены.
	АнализДанных.Вставить("ЭтоОшибкаСтруктурыДанных", Ложь); // Не найден ожидаемый элемент данных.
	АнализДанных.Вставить("Смещение", 0);
	АнализДанных.Вставить("Родители", Новый Массив);
	АнализДанных.Вставить("Буфер", ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДвоичныеДанные));
	
	Возврат АнализДанных;
	
КонецФункции

Функция АлгоритмПоOID(OIDАлгоритма, ИдентификаторыАлгоритмов, ВключаяOID)
	
	ИмяАлгоритма = ИдентификаторыАлгоритмов.Получить(OIDАлгоритма);
	
	Если ИмяАлгоритма = Неопределено Тогда
		Если ВключаяOID Тогда
			Возврат НСтр("ru = 'Неизвестный'") + " (OID " + OIDАлгоритма + ")";
		КонецЕсли;
		Возврат "";
	ИначеЕсли ВключаяOID Тогда
		Возврат СтрРазделить(ИмяАлгоритма, ",", Ложь)[0] + " (OID " + OIDАлгоритма + ")";
	Иначе
		Возврат ИмяАлгоритма;
	КонецЕсли;
	
КонецФункции

Функция ПрочитанныйБлок(АнализДанных, КлассДанных = Неопределено, ТипДанных = Неопределено, ОбязательныйБлок = Ложь)
	
	Если АнализДанных.Родители.Количество() > 0
		И АнализДанных.Смещение >= АнализДанных.Родители[0].СмещениеСледующего Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Смещение = АнализДанных.Смещение;
	
	ПропуститьНачалоБлокаИлиБлок(АнализДанных, Истина, КлассДанных, ТипДанных, ОбязательныйБлок);
	Если АнализДанных.Смещение = Смещение Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	РазмерБлока = АнализДанных.Смещение - Смещение + АнализДанных.Родители[0].РазмерДанных;
	
	Буфер = АнализДанных.Буфер.Прочитать(Смещение, РазмерБлока); // БуферДвоичныхДанных
	ПрочитанныйБлок = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(Буфер);
	ПропуститьРодительскийБлок(АнализДанных);
	
	Возврат ПрочитанныйБлок;
	
КонецФункции

Функция ПропуститьНачалоБлока(АнализДанных, КлассДанных = Неопределено, ТипДанных = Неопределено, ОбязательныйБлок = Истина) Экспорт
	
	Смещение = АнализДанных.Смещение;
	ПропуститьНачалоБлокаИлиБлок(АнализДанных, Истина, КлассДанных, ТипДанных, ОбязательныйБлок);
	
	Возврат АнализДанных.Смещение <> Смещение;
	
КонецФункции

Процедура ПропуститьБлок(АнализДанных, КлассДанных = Неопределено, ТипДанных = Неопределено, ОбязательныйБлок = Истина) Экспорт
	
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат;
	КонецЕсли;
	
	Если АнализДанных.Родители.Количество() = 0
	 Или Не АнализДанных.Родители[0].ЕстьВложения Тогда
		
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат;
	КонецЕсли;
	
	ПропуститьНачалоБлокаИлиБлок(АнализДанных, Ложь, КлассДанных, ТипДанных, ОбязательныйБлок)
	
КонецПроцедуры

Процедура ПропуститьРодительскийБлок(АнализДанных) Экспорт
	
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат;
	КонецЕсли;
	
	Если АнализДанных.Родители.Количество() < 2
	 Или Не АнализДанных.Родители[1].ЕстьВложения Тогда
		
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат;
	КонецЕсли;
	
	Если АнализДанных.Родители[0].РазмерДанных > 0 Тогда
		ОсталосьБайт = АнализДанных.Родители[0].СмещениеСледующего - АнализДанных.Смещение;
		
		Если ОсталосьБайт > 0 Тогда
			ПрочитатьБайт(АнализДанных, ОсталосьБайт);
			Если АнализДанных.ЕстьОшибка Тогда
				Возврат;
			КонецЕсли;
		ИначеЕсли ОсталосьБайт < 0 Тогда
			ПриОшибкеКодированияASN1(АнализДанных);
			Возврат;
		КонецЕсли;
	Иначе
		Пока Истина Цикл
			Если КонецБлокаНеопределеннойДлины(АнализДанных) Тогда
				Если АнализДанных.ЕстьОшибка Тогда
					Возврат;
				КонецЕсли;
				АнализДанных.Смещение = АнализДанных.Смещение + 2;
				Прервать;
			КонецЕсли;
			ПропуститьБлок(АнализДанных);
		КонецЦикла;
	КонецЕсли;
	
	АнализДанных.Родители.Удалить(0);
	
КонецПроцедуры

Процедура ПроверитьДанныеБлока(АнализДанных, СтрокаДанных)
	
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат;
	КонецЕсли;
	
	Если АнализДанных.Родители.Количество() = 0 Тогда
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат;
	КонецЕсли;
	
	РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
	Если РазмерДанных = 0 Тогда
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат;
	КонецЕсли;
	Буфер = АнализДанных.Буфер.Прочитать(АнализДанных.Смещение, РазмерДанных); // БуферДвоичныхДанных
	
	Если Буфер.Размер <> РазмерДанных Тогда
		ПриОшибкеКодированияASN1(АнализДанных);
		Возврат;
	КонецЕсли;
	АнализДанных.Смещение = АнализДанных.Смещение + РазмерДанных;
	
	СтрокаБуфера = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(Буфер);
	Если СтрокаДанных <> СтрокаБуфера Тогда
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

Функция ПрочитанныйOID(АнализДанных)
	
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если АнализДанных.Родители.Количество() = 0 Тогда
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат Неопределено;
	КонецЕсли;
	
	ЦелыеЧисла = Новый Массив;
	РазмерДанных = АнализДанных.Родители[0].РазмерДанных;
	Если РазмерДанных = 0 Тогда
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат Неопределено;
	КонецЕсли;
	ГраницаСмещения = АнализДанных.Смещение + РазмерДанных;
	
	Пока АнализДанных.Смещение < ГраницаСмещения Цикл
		ЦелоеЧисло = ПрочитатьПотоковоеЦелое(АнализДанных);
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат Неопределено;
		КонецЕсли;
		ЦелыеЧисла.Добавить(ЦелоеЧисло);
	КонецЦикла;
	
	Если АнализДанных.Смещение <> ГраницаСмещения
	 Или ЦелыеЧисла.Количество() = 0 Тогда
		
		ПриОшибкеКодированияASN1(АнализДанных);
		Возврат Неопределено;
	КонецЕсли;
	
	ЧислоSID2 = ЦелыеЧисла[0];
	Если ЧислоSID2 < 40 Тогда
		SID1 = 0;
	ИначеЕсли ЧислоSID2 < 80 Тогда
		SID1 = 1;
	Иначе
		SID1 = 2;
	КонецЕсли;
	ЦелыеЧисла[0] = ЧислоSID2 - SID1*40;
	ЦелыеЧисла.Вставить(0, SID1);
	
	СтрокиЧисел = Новый Массив;
	Для Каждого ЦелоеЧисло Из ЦелыеЧисла Цикл
		СтрокиЧисел.Добавить(Формат(ЦелоеЧисло, "ЧН=0; ЧГ="));
	КонецЦикла;
	
	Возврат СтрСоединить(СтрокиЧисел, ".");
	
КонецФункции

Процедура ПропуститьНачалоБлокаИлиБлок(АнализДанных, НачалоБлока,
			ТребуемыйКлассДанных, ТребуемыйТипДанных, ОбязательныйБлок)
	
	Если АнализДанных.Родители.Количество() > 0
	   И АнализДанных.Смещение >= АнализДанных.Родители[0].СмещениеСледующего Тогда
	
		ПриОшибкеСтруктурыДанных(АнализДанных);
		Возврат;
	КонецЕсли;
	
	СмещениеБлока = АнализДанных.Смещение;
	Байт = ПрочитатьБайт(АнализДанных);
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат;
	КонецЕсли;
	
	КлассДанных = ПобитовыйСдвигВправо(Байт, 6);
	ТипДанных = Байт - КлассДанных * 64;
	ЕстьВложения = Ложь;
	
	Если ТипДанных > 31 Тогда
		ЕстьВложения = Истина;
		ТипДанных = ТипДанных - 32;
	КонецЕсли;
	
	Если ТипДанных > 30 Тогда
		ТипДанных = ПрочитатьПотоковоеЦелое(АнализДанных);
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если ТребуемыйКлассДанных <> Неопределено
	   И ТребуемыйКлассДанных <> КлассДанных
	 Или ТребуемыйТипДанных <> Неопределено
	   И ТребуемыйТипДанных <> ТипДанных Тогда
	
		Если ОбязательныйБлок Тогда
			ПриОшибкеСтруктурыДанных(АнализДанных);
		Иначе
			АнализДанных.Смещение = СмещениеБлока;
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	РазмерДанных = ПрочитатьРазмерДанных(АнализДанных);
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат;
	КонецЕсли;
	
	Если НачалоБлока Или ЕстьВложения И РазмерДанных = 0 Тогда
		Если РазмерДанных = 0 Тогда
			Если АнализДанных.Родители.Количество() = 0 Тогда
				Если Не КонецБлокаНеопределеннойДлины(АнализДанных, Истина) Тогда
					ПриОшибкеКодированияASN1(АнализДанных);
					Возврат;
				КонецЕсли;
				СмещениеСледующего = АнализДанных.Буфер.Размер - 2;
				РазмерДанных = СмещениеСледующего - АнализДанных.Смещение;
			Иначе
				// Для блока неопределенной длины СмещениеСледующего - это только граница.
				СмещениеСледующего = АнализДанных.Родители[0].СмещениеСледующего;
			КонецЕсли;
		Иначе
			СмещениеСледующего = АнализДанных.Смещение + РазмерДанных;
			Если АнализДанных.Родители.Количество() = 0
			   И СмещениеСледующего > АнализДанных.Буфер.Размер Тогда
				
				ПриОшибкеКодированияASN1(АнализДанных);
				Возврат;
			КонецЕсли;
		КонецЕсли;
		ТекущийБлок = Новый Структура("ЕстьВложения, СмещениеСледующего, РазмерДанных",
			ЕстьВложения, СмещениеСледующего, РазмерДанных);
		АнализДанных.Родители.Вставить(0, ТекущийБлок);
		Если Не НачалоБлока Тогда
			ПропуститьРодительскийБлок(АнализДанных);
		КонецЕсли;
	Иначе
		Если РазмерДанных = 0 Тогда
			ПрочитатьКонецБлокаБезВложенийНеопределеннойДлины(АнализДанных);
		Иначе
			ПрочитатьБайт(АнализДанных, РазмерДанных);
		КонецЕсли;
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Функция КонецБлокаНеопределеннойДлины(АнализДанных, ОбщийБлок = Ложь)
	
	Буфер = АнализДанных.Буфер;
	
	Если ОбщийБлок Тогда
		Смещение = Буфер.Размер - 2;
		Если Смещение < 2 Тогда
			ПриОшибкеКодированияASN1(АнализДанных);
			Возврат Ложь;
		КонецЕсли;
	Иначе
		Смещение = АнализДанных.Смещение;
		Если Смещение + 2 > АнализДанных.Родители[0].СмещениеСледующего Тогда
			ПриОшибкеКодированияASN1(АнализДанных);
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Буфер[Смещение] = 0 И Буфер[Смещение + 1] = 0;
	
КонецФункции

Процедура ПрочитатьКонецБлокаБезВложенийНеопределеннойДлины(АнализДанных)
	
	ПредыдущийБайт = -1;
	Байт = -1;
	
	Пока Истина Цикл
		ПредыдущийБайт = Байт;
		Байт = ПрочитатьБайт(АнализДанных);
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат;
		КонецЕсли;
		Если Байт = 0 И ПредыдущийБайт = 0 Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Функция ПрочитатьПотоковоеЦелое(АнализДанных) Экспорт
	
	Целое = 0;
	Для Счетчик = 1 По 9 Цикл
		Байт = ПрочитатьБайт(АнализДанных);
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат Неопределено;
		КонецЕсли;
		Если Байт < 128 Тогда
			Целое = Целое * 128 + Байт;
			Прервать;
		Иначе
			Целое = Целое * 128 + (Байт - 128);
		КонецЕсли;
	КонецЦикла;
	
	Если Счетчик > 8 Тогда
		ПриОшибкеКодированияASN1(АнализДанных);
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат Целое;
	
КонецФункции

Функция ПрочитатьРазмерДанных(АнализДанных)
	
	Байт = ПрочитатьБайт(АнализДанных);
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если Байт < 128 Тогда
		Возврат Байт;
	КонецЕсли;
	
	КоличествоБайт = Байт - 128;
	Если КоличествоБайт = 0 Или КоличествоБайт > 8 Тогда
		Если Байт = 128 Тогда
			Возврат 0; // Блок неопределенной длины.
		КонецЕсли;
		ПриОшибкеКодированияASN1(АнализДанных);
		Возврат Неопределено;
	КонецЕсли;
	
	Целое = 0;
	Для Счетчик = 1 По КоличествоБайт Цикл
		Байт = ПрочитатьБайт(АнализДанных);
		Если АнализДанных.ЕстьОшибка Тогда
			Возврат Неопределено;
		КонецЕсли;
		Целое = Целое * 256 + Байт;
	КонецЦикла;
	
	Возврат Целое;
	
КонецФункции

Функция ПрочитатьБайт(АнализДанных, КоличествоРаз = 1)
	
	Если АнализДанных.ЕстьОшибка Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если АнализДанных.Смещение + КоличествоРаз <= АнализДанных.Буфер.Размер Тогда
		Байт = АнализДанных.Буфер.Получить(АнализДанных.Смещение + КоличествоРаз - 1);
		АнализДанных.Смещение = АнализДанных.Смещение + КоличествоРаз;
	Иначе
		Байт = Неопределено;
		ПриОшибкеКодированияASN1(АнализДанных);
	КонецЕсли;
	
	Возврат Байт;
	
КонецФункции

Процедура ПриОшибкеКодированияASN1(АнализДанных)
	
	АнализДанных.ЭтоОшибкаКодированияASN1 = Истина;
	АнализДанных.ЕстьОшибка = Истина;
	
КонецПроцедуры

Процедура ПриОшибкеСтруктурыДанных(АнализДанных)
	
	АнализДанных.ЭтоОшибкаСтруктурыДанных = Истина;
	АнализДанных.ЕстьОшибка = Истина;
	
КонецПроцедуры

Функция ИдентификаторыАлгоритмовПодписи(ТолькоАлгоритмыОткрытогоКлюча)
	
	ИдентификаторыАлгоритмов = Новый Соответствие;
	
	Наборы = НаборыАлгоритмовДляСозданияПодписи();
	Для Каждого Набор Из Наборы Цикл
		ИдентификаторыАлгоритмов.Вставить(Набор.ИдентификаторАлгоритмаОткрытогоКлюча,
			СтрСоединить(Набор.ИменаАлгоритмаПодписи, ", "));
		
		Если ТолькоАлгоритмыОткрытогоКлюча Тогда
			Продолжить;
		КонецЕсли;
		
		ИдентификаторыАлгоритмов.Вставить(Набор.ИдентификаторАлгоритмаПодписи,
			СтрСоединить(Набор.ИменаАлгоритмаПодписи, ", "));
		
		Если ЗначениеЗаполнено(Набор.ИдентификаторАлгоритмаОбмена) Тогда
			ИдентификаторыАлгоритмов.Вставить(Набор.ИдентификаторАлгоритмаОбмена,
				СтрСоединить(Набор.ИменаАлгоритмаПодписи, ", "));
		КонецЕсли;
	КонецЦикла;
	
	Возврат ИдентификаторыАлгоритмов;
	
КонецФункции

Функция ИдентификаторыАлгоритмовХеширования()
	
	ИдентификаторыАлгоритмов = Новый Соответствие;
	
	Наборы = НаборыАлгоритмовДляСозданияПодписи();
	Для Каждого Набор Из Наборы Цикл
		ИдентификаторыАлгоритмов.Вставить(Набор.ИдентификаторАлгоритмаХеширования,
			СтрСоединить(Набор.ИменаАлгоритмаХеширования, ", "));
	КонецЦикла;
	
	Возврат ИдентификаторыАлгоритмов;
	
КонецФункции

// Возвращаемое значение:
//  Массив из см. НовыйНаборАлгоритмовДляСозданияПодписи
//
Функция НаборыАлгоритмовДляСозданияПодписи() Экспорт
	
	Наборы = Новый Массив;
	
	// ГОСТ 94
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.643.2.2.20";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.643.2.2.4";
	Свойства.ИменаАлгоритмаПодписи                = ИменаАлгоритмовПодписиГОСТ_34_10_94();
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.643.2.2.9";
	Свойства.ИменаАлгоритмаХеширования            = ИменаАлгоритмовХешированияГОСТ_34_11_94();
	Свойства.ИмяАлгоритмаПодписиXML     = "http://www.w3.org/2001/04/xmldsig-more#gostr341094-gostr3411";
	Свойства.ИмяАлгоритмаХешированияXML = "http://www.w3.org/2001/04/xmldsig-more#gostr3411";
	Наборы.Добавить(Свойства);
	
	// ГОСТ 2001
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.643.2.2.19";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.643.2.2.3";
	Свойства.ИменаАлгоритмаПодписи                = ИменаАлгоритмовПодписиГОСТ_34_10_2001();
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.643.2.2.9";
	Свойства.ИменаАлгоритмаХеширования            = ИменаАлгоритмовХешированияГОСТ_34_11_94();
	Свойства.ИмяАлгоритмаПодписиXML     = "http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411";
	Свойства.ИмяАлгоритмаХешированияXML = "http://www.w3.org/2001/04/xmldsig-more#gostr3411";
	Наборы.Добавить(Свойства);
	
	// ГОСТ 2012/256
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.643.7.1.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.643.7.1.1.3.2";
	Свойства.ИменаАлгоритмаПодписи                = ИменаАлгоритмовПодписиГОСТ_34_10_2012_256();
	Свойства.ИдентификаторАлгоритмаОбмена         = "1.2.643.7.1.1.6.1";
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.643.7.1.1.2.2";
	Свойства.ИменаАлгоритмаХеширования            = ИменаАлгоритмовХешированияГОСТ_34_11_2012_256();
	Свойства.ИмяАлгоритмаПодписиXML     = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256";
	Свойства.ИмяАлгоритмаХешированияXML = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256";
	Наборы.Добавить(Свойства);
	
	// ГОСТ 2012/512
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.643.7.1.1.1.2";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.643.7.1.1.3.3";
	Свойства.ИменаАлгоритмаПодписи                = ИменаАлгоритмовПодписиГОСТ_34_10_2012_512();
	Свойства.ИдентификаторАлгоритмаОбмена         = "1.2.643.7.1.1.6.2";
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.643.7.1.1.2.3";
	Свойства.ИменаАлгоритмаХеширования            = ИменаАлгоритмовХешированияГОСТ_34_11_2012_512();
	Свойства.ИмяАлгоритмаПодписиXML     = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-512";
	Свойства.ИмяАлгоритмаХешированияXML = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-512";
	Наборы.Добавить(Свойства);
	
	// md2WithRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.2";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.840.113549.2.2";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("MD2");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	// md4withRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.3";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.840.113549.2.4";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("MD4");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	// md5WithRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.4";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.2.840.113549.2.5";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("MD5");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	// sha1WithRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.5";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "1.3.14.3.2.26";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("SHA-1");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	// sha256WithRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.11";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "2.16.840.1.101.3.4.2.1";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("SHA-256");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	// sha384WithRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.12";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "2.16.840.1.101.3.4.2.2";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("SHA-384");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	// sha512WithRSAEncryption
	Свойства = НовыйНаборАлгоритмовДляСозданияПодписи();
	Свойства.ИдентификаторАлгоритмаОткрытогоКлюча = "1.2.840.113549.1.1.1";
	Свойства.ИдентификаторАлгоритмаПодписи        = "1.2.840.113549.1.1.13";
	Свойства.ИменаАлгоритмаПодписи                = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("RSA_SIGN");
	Свойства.ИдентификаторАлгоритмаХеширования    = "2.16.840.1.101.3.4.2.3";
	Свойства.ИменаАлгоритмаХеширования            = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве("SHA-512");
	Свойства.ИмяАлгоритмаПодписиXML     = "";
	Свойства.ИмяАлгоритмаХешированияXML = "";
	Наборы.Добавить(Свойства);
	
	Возврат Наборы;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * ИдентификаторАлгоритмаОткрытогоКлюча - Строка
//   * ИдентификаторАлгоритмаПодписи - Строка
//   * ИменаАлгоритмаПодписи - Массив из Строка
//   * ИдентификаторАлгоритмаХеширования - Строка
//   * ИменаАлгоритмаХеширования - Массив из Строка
//   * ИмяАлгоритмаПодписиXML - Строка
//   * ИмяАлгоритмаХешированияXML - Строка
//    
Функция НовыйНаборАлгоритмовДляСозданияПодписи()
	
	Свойства = Новый Структура;
	Свойства.Вставить("ИдентификаторАлгоритмаОткрытогоКлюча", "");
	Свойства.Вставить("ИдентификаторАлгоритмаПодписи", "");
	Свойства.Вставить("ИменаАлгоритмаПодписи", Новый Массив);
	Свойства.Вставить("ИдентификаторАлгоритмаОбмена", "");
	Свойства.Вставить("ИдентификаторАлгоритмаХеширования", "");
	Свойства.Вставить("ИменаАлгоритмаХеширования", Новый Массив);
	Свойства.Вставить("ИмяАлгоритмаПодписиXML", "");
	Свойства.Вставить("ИмяАлгоритмаХешированияXML", "");
	
	Возврат Свойства;
	
КонецФункции

Функция ИменаАлгоритмовПодписиГОСТ_34_10_94()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.10-94"); // Представление.
	Имена.Добавить("GOST R 34.10-94");
	
	Возврат Имена;
	
КонецФункции

Функция ИменаАлгоритмовПодписиГОСТ_34_10_2001()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.10-2001"); // Представление.
	Имена.Добавить("GOST R 34.10-2001");
	Имена.Добавить("ECR3410-CP");
	
	Возврат Имена;
	
КонецФункции

Функция ИменаАлгоритмовПодписиГОСТ_34_10_2012_256()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.10-2012 256"); // Представление.
	Имена.Добавить("GR 34.10-2012 256");
	Имена.Добавить("GOST 34.10-2012 256");
	Имена.Добавить("GOST R 34.10-12 256");
	Имена.Добавить("GOST3410-12-256");
	
	Возврат Имена;
	
КонецФункции

Функция ИменаАлгоритмовПодписиГОСТ_34_10_2012_512()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.10-2012 512"); // Представление.
	Имена.Добавить("GR 34.10-2012 512");
	Имена.Добавить("GOST 34.10-2012 512");
	
	Возврат Имена;
	
КонецФункции

Функция ИменаАлгоритмовХешированияГОСТ_34_11_94()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.11-94"); // Представление.
	Имена.Добавить("GOST R 34.11-94");
	Имена.Добавить("RUS-HASH-CP");
	
	Возврат Имена;
	
КонецФункции

Функция ИменаАлгоритмовХешированияГОСТ_34_11_2012_256()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.11-2012 256"); // Представление.
	Имена.Добавить("GR 34.11-2012 256");
	Имена.Добавить("GOST 34.11-2012 256");
	Имена.Добавить("GOST R 34.11-12 256");
	Имена.Добавить("GOST3411-12-256");
	
	Возврат Имена;
	
КонецФункции

Функция ИменаАлгоритмовХешированияГОСТ_34_11_2012_512()
	
	Имена = Новый Массив;
	Имена.Добавить("ГОСТ 34.11-2012 512"); // Представление.
	Имена.Добавить("GR 34.11-2012 512");
	Имена.Добавить("GOST 34.11-2012 512");
	
	Возврат Имена;
	
КонецФункции

Функция АлгоритмПодписиСоответствующийАлгоритмуХешированияГОСТ(АлгоритмХеширования)
	
	Если Не ЗначениеЗаполнено(АлгоритмХеширования) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Соответствие = Новый Соответствие;
	Соответствие.Вставить("1.2.643.7.1.1.2.2", "1.2.643.7.1.1.1.1");
	Соответствие.Вставить("1.2.643.7.1.1.2.3", "1.2.643.7.1.1.1.2");
	
	Возврат Соответствие.Получить(АлгоритмХеширования);
		
КонецФункции

Функция СертификатСуществует(СертификатКриптографии) Экспорт
	
	Если СертификатКриптографии = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Попытка
		// Если сертификат не инициализирован, например, в контейнере подписи при отсутствии сертификата подписи, будет вызвано исключение.
		СерийныйНомер = СертификатКриптографии.СерийныйНомер;
		Возврат Истина;
	Исключение
		Возврат Ложь;
	КонецПопытки;
	
КонецФункции

#КонецОбласти

#КонецОбласти